windbg-mcp
Click 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., "@windbg-mcpanalyze the crash dump at C:\dumps\app.dmp"
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.
windbg-mcp
An MCP server that exposes WinDbg/DbgEng to AI agents (Claude Code, Claude Desktop, Cursor, …) over stdio. It drives a live debugger engine for user-mode, kernel-mode, crash-dump, and Time Travel Debugging (TTD) workflows.
The low-level engine bindings live in win-kexp
(src/dbgeng.rs); this crate adds a dedicated engine thread and the rmcp tool surface on top.
Architecture
engine.rs— DbgEng requires serialized, single-thread access (WaitForEventmust run on the session-owning thread), so theDebugEngineis created on, and confined to, one OS worker thread. Async tool handlers marshal closures onto it via anmpscchannel withoneshotreplies and a per-call timeout. Acatch_unwindguard turns a panic in one operation into a failed call rather than a dead thread.server.rs— the MCP tools (see below), built withrmcp's#[tool_router]/#[tool_handler].ttd.rs— locatesTTD.exeand launches trace recording.main.rs— tokio + stdio transport. Logs go to stderr (stdout is the JSON-RPC channel).
MCP protocol revision: built on rmcp 3.x, this server accepts every revision that SDK knows —
2026-07-28 and the initialize-handshake ("legacy") era before it (2025-11-25, 2025-06-18,
2025-03-26, 2024-11-05) — and serves whichever the client selects. A 2026-07-28 client gets the
stateless, per-request model (server/discover, resultType, per-request _meta) and may open with
server/discover instead of initialize; older clients keep the handshake, and a client that offers
an unknown revision is answered with 2025-11-25.
Related MCP server: dump-analyzer-mcp-server
Requirements
Windows x64 (host bitness must match the target).
dbgeng.dll/dbghelp.dll— present inSystem32on modern Windows 11 (verified with10.0.26100). This is enough for live user-mode/kernel debugging and crash-dump analysis.For crash-dump
!analyze(and any other!-extension command), the engine needs the WinDbgwinext\extensions bundled next to the binary — System32's engine ships none, so!analyzewould return "No export analyze found". See Bundling the WinDbg engine below.For Time Travel Debugging (
.run) replay, the System32 engine is not enough — it rejects.runtraces (0x80070057). You need the WinDbg engine (which bundles the TTD replay components) loaded next to the binary — see TTD engine below.TTD.exe(the standalone Time Travel Debugging recorder) forrecord_trace— ships with the WinDbg / TTD store packages; put it onPATH.A reachable symbol server (e.g.
srv*https://msdl.microsoft.com/download/symbols) for symbol-name queries likettd_calls("ucrtbase!_stdio_common_vfprintf"). Offline, address-based queries and the data model still work; symbol names won't resolve.Administrator for live kernel debugging and TTD recording (not for replay).
Build or download
Prebuilt Windows x64 binaries are attached to each
GitHub release as
windbg-mcp-vX.Y.Z-windows-x64.zip (with a SHA256SUMS.txt to verify the download
against — the skill's setup.md snippet does this for you) — no Rust toolchain needed.
To build from source instead:
cargo build --releasewin-kexp is fetched automatically as a git dependency from glslang/win-kexp — no sibling checkout needed.
Bundling the WinDbg engine
Needed for two things: TTD .run replay (System32's engine rejects traces with 0x80070057) and
crash-dump !analyze (which lives in the winext\ extensions that System32 doesn't ship).
DebugCreate binds to whichever dbgeng.dll the loader finds first, and the app directory is
searched before System32, so the copied WinDbg engine (which replays TTD traces and ships the
extensions) wins. One-time, from the installed WinDbg store package:
$wd = (Get-AppxPackage Microsoft.WinDbg).InstallLocation + "\amd64"
$dst = "C:\workspace\windbg-mcp\target\release"
Copy-Item "$wd\dbgeng.dll","$wd\dbghelp.dll","$wd\dbgcore.dll","$wd\dbgmodel.dll",`
"$wd\symsrv.dll","$wd\msdia140.dll" $dst -Force
Copy-Item "$wd\ttd" "$dst\ttd" -Recurse -Force # TTDReplay*.dll, TtdExt.dll, TTDAnalyze.dll, ...
Copy-Item "$wd\winext" "$dst\winext" -Recurse -Force # ext.dll (!analyze), kext.dll, … — crash-dump triageThe
ttd\subdir provides the@$cursession.TTD/@$curprocess.TTDdata model and the!tttime-travel commands.The
winext\subdir providesext.dll(which exports!analyze) and the other!-extensions.open_dumpruns.load extfor you, but note the **unqualified!analyzedoes not resolve** on this minimal engine — use the module-qualified **!ext.analyze -v** for crash-dump triage. Withoutwinext\,!analyzereturns "No export analyze found".msdia140.dllis required for PDB symbols. Without it,dbghelpcan't parse any PDB (dia error 0x8007007e) and silently falls back to export symbols — which makesmodule!namelookups (and sottd_calls("ucrtbase!__stdio_common_vfprintf")) fail even with the right PDB in the cache.symsrv.dllis needed to read a symbol-store cache.
(cargo clean wipes target\, so re-copy after one.) Live and dump debugging work with or without
the TTD engine; PDB symbol names* need msdia140.dll + a symbol path
(execute → `.sympath srvC:\ProgramData\Dbg\sym*https://msdl.microsoft.com/download/symbols`).
Use with an MCP client
Point your client at the built binary, e.g. Claude Code:
// .mcp.json (or claude_desktop_config.json under "mcpServers")
{
"mcpServers": {
"windbg": {
"command": "C:\\workspace\\windbg-mcp\\target\\release\\windbg-mcp.exe"
}
}
}As a Claude Code plugin
This repo is also a single-plugin Claude Code marketplace:
installing it registers the windbg MCP server and a windbg-debugging skill that
knows how to drive it (setup, crash-dump, live/kernel, and TTD playbooks).
/plugin marketplace add glslang/windbg-mcp
/plugin install windbg-mcp@windbg-mcpThe plugin ships source, not a binary, so after installing you still put the server binary in
place — download a prebuilt release or build from source — and (for .run replay and
crash-dump !analyze) bundle the WinDbg engine — the skill's setup.md
walks through it, and it mirrors the Build or download and
Bundling the WinDbg engine sections above. Then /reload-plugins
to connect the server. The plugin points at ${CLAUDE_PLUGIN_ROOT}/target/release/windbg-mcp.exe.
From the official MCP registry
The server is listed in the official MCP registry as
io.github.glslang/windbg-mcp. Clients that support the registry — or that install
MCPB bundles directly — can add it by name: the client
downloads that release's .mcpb bundle, verifies its SHA-256, and wires up the windbg-mcp.exe
inside it as an stdio server, with no Rust build or manual binary placement.
The bundle is Windows x64 only and ships just the server binary, so the one-time engine setup
still applies — for TTD .run replay and crash-dump !analyze, drop the WinDbg engine DLLs next
to the client-extracted windbg-mcp.exe (the skill's setup.md covers it). Basic live and
crash-dump work runs on the in-box System32 engine without them.
Releasing
The plugin sets an explicit version in
.claude-plugin/plugin.json, so users only receive an update
when that version changes — pushing commits alone does not trigger one. To cut a release, bump
version in plugin.json and Cargo.toml, bump the release badge near the top of this README,
add a matching entry to
CHANGELOG.md, and tag the commit vX.Y.Z. Run
claude plugin validate . --strict before publishing. Pushing the tag runs
release.yml, which verifies the tag matches both manifest
versions and the README badge, builds windbg-mcp.exe, and attaches the zip + SHA256 checksum to the GitHub release.
It also builds an MCPB bundle
(windbg-mcp-vX.Y.Z-windows-x64.mcpb, described by
packaging/mcpb/manifest.json) and publishes a
server.json entry to the official MCP Registry
(io.github.glslang/windbg-mcp) with the mcp-publisher CLI over GitHub OIDC — no secrets. CI
stamps the release version into both files and the bundle's SHA-256 into server.json, so
neither is part of the manual bump list above.
The zip also gets a signed
build-provenance attestation
tying it to the workflow run that built it — verify with:
gh attestation verify <zip> --repo glslang/windbg-mcp `
--signer-workflow glslang/windbg-mcp/.github/workflows/release.yml(--repo alone only proves the attestation came from some workflow in this repo;
--signer-workflow pins it to the release workflow.)
Walkthroughs
docs/crash-dump-walkthrough.md— triaging a real kernel minidump (docs/samples/052126-34312-01.dmp): a0x9F DRIVER_POWER_STATE_FAILUREtraced tonvlddmkm.sysvia!ext.analyze -vand a manual device-stack walk, with the real outputs and the partial-minidump (0x80040205) gotcha.docs/ttd-walkthrough.md— a hands-on tour of the TTD tools against thexusheng6/TTD_labhelloworldsample: opening a.run, surveying events/threads, forward/reverse navigation, memory analysis, and countingprintfcalls with symbols (with the real outputs and the gotchas). It maps each tool to the lab's exercises.docs/flareauthenticator-ttd-walkthrough.md— a full TTD → Z3 solve of an obfuscated Qt crackme (Flare-On 12 #8). Defeats an anti-analysis env guard, records a wrong-guess run, and usesttd_calls/ttd_memory/reverse-navigation to peel control-flow flattening + computed calls + encrypted strings down to the exact check: a per-keystroke rolling hash that reduces to a pure weighted sum. The 25 weights come from the replay, the 250gvalues from debugger function-evaluation, and Z3 finds a satisfying code — which reveals the flag (the code is intentionally non-unique). Runnable solver inexamples/flareauthenticator/; recorded terminal session indocs/flareauthenticator.cast(asciinema play) — rendered as a GIF in the walkthrough.docs/driver-ioctl-walkthrough.md— enumerating a driver's IOCTL surface and deciding user-mode reachability on a live KDNET kernel:driver_object/ufto recover the\Driver\mountmgrdispatch switch,decode_ioctlfor the access tiers, the device DACL parsed from memory, and anioctl_tracesweep — ending with a reachability report (which codes a standard user can reach vs. what the I/O manager blocks).
Tools
Group | Tools |
Session |
|
State |
|
Control |
|
TTD nav |
|
TTD analysis |
|
Driver IOCTL |
|
Raw |
|
Session handles
The six Session tools that open a target (open_dump, open_trace, attach_kernel_local,
attach_kernel, attach_process, launch) return a session_id. Every tool that touches the
debug target accepts it as an optional argument and refuses to run if it no longer matches the
session the engine is attached to.
This matters because one server process drives one DbgEng session, while an MCP connection is not
a session: a client may interleave unrelated requests over the same stdio process. Without a handle,
a read_memory issued after someone else's open_dump silently reads the wrong target. Passing
session_id turns that into a visible error instead.
The check runs on the engine thread, in the same queued job as the debugger call it guards.
That ordering is what makes it sound: checking on the caller side would leave a window in which a
concurrently-issued open_dump is queued ahead of a call that already passed validation, so the
call would still execute against the replaced target.
The guarantee is detection, not exclusion: the opening tools deliberately take no session_id, so
holding a handle does not stop another caller replacing your target — it guarantees that any later
call of yours which supplies the handle fails loudly instead of acting on a target you did not open.
Two caveats, both in the command hatches. The typed tools announce their own transitions, but
execute can replace the target directly (.opendump, .attach, .detach, .kill, .restart,
.abandon, .remote, q/qd/qq), and those commands retire the current handle. dx is the
second hatch: the data model reaches Debugger.Utility.Control.ExecuteCommand, which runs any
command, so an expression that touches command execution retires the handle too — conservatively,
since the command it runs is a runtime string this server never sees.
Both matches are deliberately biased toward retiring: over-matching costs one re-open,
under-matching would let a stale handle through. Neither can be exhaustive — DbgEng has more ways to
reach the target than a name list can enumerate, and the data model is extensible — so inside
execute and dx a handle is a strong hint rather than a guarantee. Everywhere else it is a
guarantee.
The argument is optional — omit it and a call operates on whatever session is current, exactly as
before. decode_ioctl (pure) and record_trace (independent of the debug session) do not take it.
If an open times out, do not open again — recover instead. The per-call timeout abandons the wait,
not the job, so the open may still be running and may still commit a handle no reply carried. A live
attach_kernel is the case that matters: it waits indefinitely by design, so a call reporting a
timeout while the attach completes moments later is normal (see Limitations & notes).
A timed-out open therefore names the handle it would commit, and session_status answers for
it: pass that id and it reports whether the open is still pending, has landed, or has
failed.
That three-way answer is the point. The current handle alone cannot distinguish them — "not yours" is equally true while you wait and after you have failed — and the two cases need opposite responses. While pending, re-running the open would attach to or start a second target. Once failed, opening again is the only way forward. Guessing either way is a real mistake, so the server tracks the outcome rather than leaving you to infer it.
Only the last few settled opens are remembered — an open still in flight is never forgotten, so an
id session_status does not recognise is one that finished a while ago, and re-opening is safe.
session_status does not queue on the engine thread, so it still answers while a parked attach
holds it.
Typed operands are operands, not commands
The typed tools build debugger commands by interpolation (u {address}, bp {expression},
!drvobj {name} 7), so those parameters refuse ;, line breaks, and " — the last everywhere
except dx, whose data-model expressions use quoted literals legitimately.
Two things go wrong without that. DbgEng treats ; as a command separator, so
disassemble { address: "rip; .opendump C:\other.dmp" } would replace the debug target from a tool
that reports itself read-only. And bp <location> "command" is real WinDbg syntax — ioctl_trace
builds exactly that form — so a quote in a breakpoint location arms a command that runs on every
hit, replacing the target at some arbitrary later moment, outside any tool call and outside anything
that could retire the session handle.
Nothing legitimate is lost: these parameters were always single operands. Use execute to run a
command list — it is annotated destructive and retires the handle when a command changes the target.
Error reporting
A failed debugger operation — an unresolvable symbol, an unreadable address, a target that never
stopped — comes back as a normal tool result with isError: true and the debugger's own text, so
the model can read it and correct itself. Only a dead engine thread is reported as a JSON-RPC
protocol error.
The forward (go/step_over/step_into) and reverse (reverse_go/step_over_back/step_back)
control tools mirror a debugger UI's F9/F8/F7 and Shift+F9/F8/F7, so an agent can drive a trace in
both directions and jump anywhere with goto_position. All of these issue the command and pump the
engine to the next stop (a plain Execute only sets the run state — it doesn't move the target),
which is what makes both live stepping and TTD forward/reverse navigation actually advance.
ttd_calls/ttd_memory/ttd_events are convenience wrappers over the TTD data model: ttd_calls
and ttd_memory query @$cursession.TTD.{Calls,Memory} (every call to a function / every access to
an address range), and ttd_events queries @$curprocess.TTD.Events (the module/thread/exception
timeline). For anything else, dx evaluates arbitrary data-model/LINQ expressions, e.g.
@$cursession.TTD.Calls("ntdll!NtCreateFile").Where(c => c.ReturnValue != 0).
Limitations & notes
TTD is user-mode only (a Microsoft limitation): kernel debugging and TTD are distinct session types — you can't time-travel a kernel target.
launchandattach_processstop the target at its initial/break-in point with a live process/thread context. (The binding enables the initial-breakpoint event filter —sxe ibp— which a bareDebugCreatehost leaves off; without it the target would run free.) Note: on Windows 11notepadis a Store app, so attaching by the PID thatStart-Process notepadreturns can hit0xD000010A(that PID is a transient launcher) — attach to a classic Win32 process.read_memorytakes a numeric/0x-hex address only; for register/symbol expressions useexecutewithdb/dd(e.g.db @rip).reachable_from_dispatchis a static call-graph walk overufdisassembly: it follows direct calls and cross-function tail jumps but not indirect calls through function pointers or unresolved compiler jump tables. So aREACHABLEverdict is sound (a concrete static path exists, and the path is reported), whileNOT REACHABLEis best-effort within the explored bounds. If the dispatch uses aswitch(IoControlCode)jump table (common), pass the specific handler VA asfromto scope past it, or confirm dynamically with a breakpoint +go.Crash-dump triage uses
!ext.analyze -v, not!analyze— the bundled engine only resolves the module-qualified form (see Bundling the WinDbg engine). On a partial minidump, reads of pages that weren't captured raiseAn unexpected exception was raised (0x80040205)rather than a clean "memory read error"; query the specific field you need (e.g.dt nt!_DRIVER_OBJECT <addr> DriverName) instead of dumping whole structures. See the crash-dump walkthrough.Single-stepping is only valid once the target is stopped with a real thread context (after a
go/step or a breakpoint hit). Stepping straight after a baregoto_positionto the very start of a trace (before any thread is live) returns0x80040205—goto a breakpoint first.Symbol names (
module!func) need (a)msdia140.dllbundled next to the binary, (b) a symbol path pointing at the PDBs (.sympath …), and (c) for TTD, reloading at a stopped position (after ago/breakpoint, not straight off a!tt) so the module's PDB is matched and loaded. With those, e.g.ttd_calls("ucrtbase!__stdio_common_vfprintf")returns the exact call count. Without symbols, the data model, navigation, and memory reads still work — query by address.One debug session, one command at a time. A single engine instance runs on a dedicated thread and processes operations serially. Issue tool calls sequentially — await each result before sending the next; the server does not order concurrent in-flight requests, so pipelining them (firing several calls before their results return) can run a command before the one that establishes its state (a call racing ahead of
open_dumpfails with0x80040205), and is meaningless for stateful, order-dependent debugger commands anyway. Standard MCP clients already serialize call→result, so this is only a concern for custom/batched callers. End a session withend_sessionbefore opening another target.TTD replay (
open_trace) needsTTDReplay.dlldiscoverable but not elevation; TTD recording (record_trace) needsTTD.exeand Administrator.record_tracecaptures the recorder's startup output to<out_dir>\ttd_record.logand watches it briefly, so a fast failure (e.g. running un-elevated →0x80070005 Access is denied) is reported as an error rather than a false "recording started".Control-flow tools (
go/step*) issue the corresponding debugger command; precise stop/wait semantics for long-runninggoagainst a live target are bounded by the per-call timeout.
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 Servers
- Flicense-qualityDmaintenanceAn MCP server for debugging Windows processes using WinDbg and CDB. It enables users to attach to processes, manage breakpoints, inspect memory, and control execution flow through natural language.Last updated2
- Alicense-qualityCmaintenanceMCP server for remote Windows Crash Dump analysis. Enables AI agents to analyze crash dumps via CDB commands through standard MCP interfaces.Last updated2MIT
- Alicense-qualityCmaintenanceMCP server for AI-assisted Python debugging using debugpy and Debug Adapter Protocol, enabling AI agents to run tests, set breakpoints, and inspect variables via natural language.Last updated8MIT
- AlicenseBqualityBmaintenanceMCP server that wraps gdb to enable LLMs to drive live debugging sessions, including starting sessions on binaries, attaching to processes, and running commands.Last updated73MIT
Related MCP Connectors
MCP server exposing the Backtest360 engine API as tools for AI agents.
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
Local-first RAG engine with MCP server for AI agent integration.
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/glslang/windbg-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server