Changed
-
Runs on the mcp 2.x SDK - the requirement is now
mcp>=2.0.0, up from the<2.0.0cap
1.0.1 shipped as a stopgap. 2.0.0 removed the low-level@server.list_tools()/
@server.call_tool()decorators this server registered its tools through, so the handlers are
now passed toServer(...)directly and return the SDK's result types (ListToolsResult,
CallToolResult) instead of bare lists.McpErrorisMCPError, and it takes a code and a
message rather than anErrorData. None of this is visible to clients: the same nine tools
with the same schemas, over both stdio and streamable-http (#78).Installing mcp-windbg pulls the SDK it needs, so there is nothing to do on upgrade. Python
support is unchanged - the 2.x SDK requires 3.10+, exactly as this project already did. -
Every runtime dependency is capped at the next major -
mcp<3.0.0,pydantic<3.0.0,
starlette<2.0.0,uvicorn<1.0.0. #76 happened becausemcp>=1.28.1had no upper bound, so
the 2.0.0 release landed in fresh installs and broke them at import; moving the floor to 2.x
without a ceiling would have left the identical trap for 3.0.0. mcp-windbg is an application
rather than a library, so an upper bound cannot cause a diamond conflict for anyone, and it
turns the next breaking SDK release into a Dependabot PR that fails CI instead of an install
everybody has to work around by hand. -
A weekly canary tests the versions users will actually get - CI installs from
uv.lock, so
it only ever proved the server works against the exact versions pinned there, while a PyPI
install resolves to whatever the ranges allow.dependency-canary.ymlcloses that gap: it
resolves to the newest allowed versions, ignoring the lock, and runs the hermetic suite against
them every Monday. It is scheduled rather than attached to pull requests on purpose - an
upstream release breaking the build should page the maintainer, not block someone's unrelated PR.
Added
wait_for_break- block until a target you resumed stops again (bugcheck, breakpoint, or a
CTRL+BREAK from elsewhere) and return everything the debugger printed when it did. If the wait
expires the target is left running, so waiting never halts a machine behind your back.
Fixed
gno longer freezes the target it was supposed to release - go-class commands (g,gh,
gn,gN,gc,gu) hand the CPU back to the target, after which the debugger stops reading
its stdin. The marker protocol queued an.echothe debugger could not answer, so the command
hit its timeout and the cancel-on-timeout CTRL+BREAK halted the target again - the exact
opposite of what was asked. They are now written bare and return immediately. The step family
(p,t,pa,ta, ...) is unaffected: it returns to the prompt on its own and keeps the
marker round-trip (#74).- An ordinary command after
gbreaks in by itself - no need to interleavesend_ctrl_break
manually, and whatever the target printed on the way to stopping leads that command's output
instead of being discarded. - A break-in issued the instant
greturns is no longer swallowed - resuming wrote theg
and returned without waiting for the debugger to read it, so for a moment the debugger was
still sitting at its prompt with the resume unread. A CTRL+BREAK arriving in that window was
answered by the prompt instead of reaching the target, and the break was simply lost: the
caller then waited out a fullwait_for_breaktimeout on a machine nothing was going to stop.
Measured against a live KDNET target, a break sent with no gap aftergwas lost every single
time. The resume is now confirmed consumed before it is reported. A go-class command that stops
again at once - agureturning in microseconds, a breakpoint hit immediately - reports what it
printed instead of claiming the target is running. - Break-in asks before it signals - a CTRL+BREAK aimed at a target that has in fact already
stopped queues a break request a kernel target honours later, re-halting a machine we thought
we had released. The session probes for a prompt first and only signals if that goes
unanswered, which also makessend_ctrl_breaksafe to issue speculatively. wait_for_breaktrusts the debugger, not a flag - it is right about a target running for
reasons this server never caused, and a secondgwhile the target really is still running is
refused rather than queued behind the first.bp nt!X; greports whether the breakpoint was set - the part before thegruns with its
output returned, so a typo'd symbol surfaces asCouldn't resolve errorrather than as a wait
for a break that can never arrive. A;inside a quoted command string is left alone.- Output is no longer lost at a deadline - a marker that lands in the moment between a timeout
expiring and being handled now counts as arrived, and the break-in output a timed-out command
was carrying rides along on the error instead of vanishing with it. - One operation at a time per session -
wait_for_breakparks for minutes on a worker thread
so the rest of the server keeps answering; a second call against the same session is refused
at once, with a message pointing atsend_ctrl_break, rather than interleaving markers and
returning each other's output. The refusal never waits, so it cannot stall the server it exists
to keep responsive. - Closing a session ends a wait parked on it - instead of leaving a thread sitting out its
full timeout on a debugger that no longer exists and then reporting the target as still running. - Kernel sessions over a named pipe or serial cable connect -
kdannounces a COM/pipe link
withKernel Debugger connection established, not the KDNETConnected to target .... Only the
latter was matched, soopen_kd_sessiontimed out on a target that was in fact attached
(#47, #74).