ntdrive
One line: This server lets an LLM agent drive a VMware Workstation Windows guest end to end — power and snapshots, kernel debugging with kd.exe, SSH terminal sessions, and console screenshots — through a local daemon.
VM lifecycle: list VMs and their state (
vm_list,vm_state), start/resume, suspend, stop (soft or hard withconfirm=true), and reboot (soft, hard, or from the debugger, with optional kd reattach and terminal reopen).Snapshots: list the snapshot tree with metadata, take snapshots (memory included while running), revert (automatically detaching/reattaching the debugger and reopening terminals), and delete (with
confirm=true, optionally the whole subtree).Kernel debugging over KDNET or serial: prepare host transport (
kd_setup_host), enable debugging in the guest via bcdedit (kd_setup_guest), attach/detachkd.exe, break in, resume, run arbitrary debugger commands, wait for a bugcheck or breakpoint, and inspect debugger state and the log tail.SSH terminals: open PTY sessions (powershell/cmd/pwsh, resizable), send text and key tokens like
{ctrl+c}, read deltas or a rendered screen, wait on a regex, run a command and get its output plus exit code, list sessions, and close them.Files: push files/directories/globs into the guest (with optional SHA-256 verification) and pull files back out, without needing a shell.
Console: capture a PNG screenshot of the VM console (optionally base64), useful for BSOD or login screens.
Unified state and health: one
sys_statecall returns power, debugger and terminal state;sys_healthchecks host binaries, config and each VM's SSH port and debug transport.Cross-tool consistency: the daemon keeps state in sync — while the debugger is broken in the guest is frozen, so terminal/file tools fail fast instead of hanging; reverts and reboots reattach kd and reopen terminals.
Provides tools for managing VMware Workstation virtual machines, including power operations, snapshot management, console screenshots, file transfer, and kernel debugging via KDNET.
Click on "Deploy 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., "@ntdrive revert to the last good snapshot and power on the VM"
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.
A VMware Workstation Windows guest, driven by an LLM agent: power, snapshots, kernel debugging with kd.exe, and a live SSH terminal, behind one daemon.
English | Korean
Drive a Windows guest on VMware Workstation from an LLM agent: power and snapshots, kernel
debugging with kd.exe over KDNET or a serial pipe, and a real-time SSH terminal, behind one local
daemon and one set of tools exposed as an MCP server, a CLI and a Python SDK. It is built for the
kernel driver and Windows security loop: build, deploy to the guest, load, hit a crash or
breakpoint, analyze in the debugger, revert a snapshot, repeat. Each step is a tool call, and the
daemon keeps the pieces consistent so the agent does not have to. Windows only. The name is the
point: NT is the Windows kernel, and ntdrive drives it.
Needs: a Windows 11 host with VMware Workstation Pro 17.6 or newer, the Debugging Tools for
Windows (kd.exe and kdnet.exe from the Windows SDK or WDK), Python 3.12 and uv. Guest: Windows
10 or 11 x64 with VMware Tools, Secure Boot off and, for KDNET, the e1000e NIC. No administrator
shell on the host.
Fastest path in (guest first, then host):
In the guest, copy
scripts\setup-guest.cmdin and run it.On the host:
git clone https://github.com/jiy2745/ntdrive cd ntdrive scripts\setup-host.cmdIt ends with
ntdrive verifyand prints ALL SET.Point an MCP client at the installed
ntdrive-mcpcommand (a clone carries.mcp.json).
Read next: SKILL.md if you are an agent using the tools, AGENTS.md if you are changing this
code, PRD.md for the requirements. The full map is under Documentation map below.
Why it exists
An agent that wants to debug a driver needs three windows at once: the VMware controls, WinDbg, and an SSH or RDP session into the guest. ntdrive exposes all three as tools and, more importantly, keeps their state in sync. Revert a snapshot and the debugger reattaches and the terminal reconnects on its own. Break into the debugger and the terminal tools refuse to hang, because the guest is frozen. That cross-tool consistency is the part existing debugger-only or VM-only tools leave to you.
Related MCP server: VMware MCP Server
How it fits together
Claude Code (agent) Human / CI (shell) pytest / automation
| stdio | argv | import
v v v
ntdrive-mcp ntdrive CLI Python SDK (NtDrive)
| | |
+------------------------+-----------------------+
| HTTP + WebSocket, 127.0.0.1, token
v
+------------- ntdrived (daemon, holds every session) -------------+
| ToolRegistry -> StateStore -> Orchestrator -> AuditLog |
| VmwareAdapter (vmrun) KdSession (kd.exe) TermManager (SSH) |
+---------+------------------------+-----------------------+--------+
| vmrun.exe | UDP KDNET | TCP 22 (SSH PTY)
v v v
+------------------------ VM (Windows guest) -----------------------+
| KDNET (boot-time) OpenSSH -> PowerShell PTY VMware Tools |
+-------------------------------------------------------------------+The daemon owns the sessions. The MCP server, the CLI and the SDK are thin, stateless clients of it,
so an agent, a person at a shell, and a test script all see the same live sessions. Every tool is
declared once in a single registry (ntdrive.core.registry) and the three front doors are generated
from it, so they never drift apart.
Setup
Host: Windows 11, VMware Workstation Pro 17.6 or newer, the Debugging Tools for Windows (kd.exe
and kdnet.exe from the Windows SDK or WDK), Python 3.12 and uv. No
administrator shell is needed. Guest: Windows 10 or 11 x64 with VMware Tools, Secure Boot off in
the VM settings (bcdedit /debug on needs that) and, for KDNET, the e1000e NIC. Guest first,
then host: the host script ends with the end-to-end check and reboots the guest itself when the
debugger needs it. ntdrive sys health names the fix for anything that is missing.
1. Guest. Copy scripts\setup-guest.cmd into the guest (drag and
drop works once VMware Tools are in) and run it from any shell or by double click. It asks
for administrator rights itself (one UAC click), creates the local administrator ntdrive and
asks for its password (type the same one in ntdrive setup on the host), installs OpenSSH Server
with PowerShell as the default shell, opens port 22 and turns on KDNET. The host IP comes from the
NAT gateway and the key is generated in the guest, so nothing is copied by hand. Running it again
is safe. Reboot the guest when it says so.
2. Host. One command from a clone:
git clone https://github.com/jiy2745/ntdrive
cd ntdrive
scripts\setup-host.cmdIt runs uv sync, puts the ntdrive, ntdrive-mcp and ntdrived commands on your PATH (an
editable uv tool install, so they follow the clone), then for each VM you pick from the VMware
library asks for the guest account and passwords (masked while you type, stored as User
environment variables, never in a file), restarts the daemon, repairs the host firewall for KDNET
through one UAC prompt and ends with ntdrive verify. The config lands in
%LOCALAPPDATA%\ntdrive\vms.yaml, and vms.example.yaml documents every field. Without a clone:
uv tool install git+https://github.com/jiy2745/ntdrive, then ntdrive setup.
3. Verify. The host script already ran this. Run it again after any change, or whenever the guest was set up after the host:
ntdrive verify # config, power, SSH login, firewall, then attach, break in, resumeIt ends with ALL SET or the first thing to fix and how. When the guest was configured a moment
ago it reboots the guest itself so the debugger connects.
Options, all optional:
Where | Switch | What it does |
guest |
| use your own Windows account for SSH instead of creating |
guest |
| skip |
guest |
| OpenSSH from the Win32-OpenSSH zip, for a guest without internet (Insider builds have no capability package, and the script falls back to a download on its own) |
guest |
| serial named-pipe transport instead of KDNET |
host |
| only the end-to-end check |
host |
| manual repair of the KDNET firewall rules, from an Administrator shell |
host |
| serial transport for a host where nobody can approve a UAC prompt, then |
Several VMs: run setup-host.cmd again (or ntdrive setup) for each VM and setup-guest.cmd in
each guest. Every guest picks its own KDNET port from its machine id, and the host moves a guest
whose port collides with another VM's. A guest set up by hand gets its KDNET settings from
ntdrive kd setup-guest <vm>. scripts\probe-guest.ps1 prints what a guest has (build, Secure
Boot, NIC model, OpenSSH state) before you run the setup script.
4. MCP clients. The server is the installed ntdrive-mcp command: stdio, no arguments, no
environment variables (the config lives in %LOCALAPPDATA%\ntdrive), and it starts the daemon
itself. Set the client's MCP tool-call timeout above 600 s, because the wait tools long-poll.
Claude Code: a clone carries .mcp.json, elsewhere claude mcp add ntdrive -- ntdrive-mcp. Allow
the tools with the permission rule mcp__ntdrive__*.
{ "mcpServers": { "ntdrive": { "command": "ntdrive-mcp" } } }Claude Desktop (%APPDATA%\Claude\claude_desktop_config.json) does not search PATH, so give the
full path. uv tool dir --bin prints the directory, %USERPROFILE%\.local\bin by default:
{ "mcpServers": { "ntdrive": { "command": "C:\\Users\\you\\.local\\bin\\ntdrive-mcp.exe" } } }VS Code (.vscode/mcp.json), and Cursor takes the Claude Desktop shape in .cursor/mcp.json:
{ "servers": { "ntdrive": { "type": "stdio", "command": "ntdrive-mcp" } } }Quick start (CLI)
The CLI has the same tools as subcommands. The first call auto-starts the daemon.
The daemon runs windowless (started with pythonw.exe, detached), and the vmrun and kd.exe
processes it starts are hidden too, so nothing pops up on the desktop. ntdrive daemon status
says whether it is up. ntdrive daemon logs -f follows what it is doing: one line per tool call
(name, caller, outcome, never the arguments) plus the daemon's own messages, from
%LOCALAPPDATA%\ntdrive\logs\daemon.out.log.
ntdrive sys health # host binaries and config, then each VM live: power, SSH, debugger transport
ntdrive vm start win11
ntdrive term open win11 # prints a session id and a CoView URL, a browser page that mirrors the session
ntdrive kd attach win11 # serial: attaches at once. net: the target connects while the guest boots
ntdrive kd break win11 # freezes the guest at a kd> prompt
ntdrive kd exec win11 "!process 0 0"
ntdrive kd go win11 # resume the guestAdd --json to any command for the raw tool result. Exit codes: 0 ok, 1 error, 2 bad arguments,
3 confirmation required, 4 guest frozen by the debugger, 5 timeout.
A person can sit down in a session the agent opened:
ntdrive term attach <session-id> # Ctrl+] to detach, your keystrokes are logged as humanThe Python SDK has the same tools as methods, vt.<group>.<verb>(...):
from ntdrive.sdk import NtDrive
vt = NtDrive() # talks to the daemon, starts it if needed
vt.vm.start("win11")
print(vt.sys.state(vm="win11")) # sys_state takes keyword arguments onlyWhat an agent can do
VM power and snapshots: start, stop, suspend, three reboot modes, and live snapshots with a tree listing, revert, and delete.
Kernel debugging over KDNET or a serial pipe: set up the guest, attach
kd.exe, break in, run debugger commands, wait for a bugcheck or breakpoint, and detach.Real-time terminal: open SSH PTY sessions, stream output, render the screen, wait on a regex, and send keys including
{ctrl+c}.Console and files: capture a console screenshot (for a BSOD or login screen) and copy files both ways with checksum verification.
Unified state: one
sys_statecall returns VM power, debugger state and terminal sessions, and compound actions like snapshot revert run as a single orchestrated step.
The full list below is generated from the tool registry (scripts/tools_table.py --write README.md refreshes it, a test keeps it current). Effect is what the MCP annotations say: a read tool
changes nothing, an additive one adds or starts something, a destructive one can discard state,
and the ones that need confirm=true say so in their arguments. Arguments are in PRD.md section
7 and in ntdrive <group> <verb> --help.
Tool | Effect | What it does |
| read | List Windows 11 Insider Preview builds (amd64) that vm_upgrade can install, newest first, from the UUP dump API. |
| destructive | Upgrade a running guest in place to a Windows Insider build. The guest downloads the build from Microsoft, builds setup media and runs setup.exe as SYSTEM, which reboots it several times over 30 to 90 minutes. Returns at once: poll vm_upgrade_status. Edition and language follow the guest. Take a snapshot first, and kd_detach (refused while kd is attached). Needs 30 GB free on C:. confirm=true. |
| read | Where a vm_upgrade stands: building (with the step), upgrading, offline (setup is between reboots), done, failed (with setup's own error lines), interrupted or none. When done it names the steps that finish the job: kd_setup_guest, vm_reboot, kd_attach, a snapshot. |
| read | List registered VMs with power, debugger and terminal state. |
| read | Power, debugger and terminal state of one VM. |
| read | Wait until the guest is back up: block until SSH answers, or the timeout passes. For after a reboot or a bugcheck's auto-restart, so no manual polling loop is needed. |
| additive | Power on (or resume) a VM without the GUI by default. discard_saved_state boots fresh when a stale saved state blocks the resume. |
| destructive | Stop the VM: mode soft, hard or kill. hard and kill need confirm=true. |
| destructive | Reboot the guest (soft, hard or from the debugger) and bring kd and terminals back, the terminals under new session ids. The guest comes back running rather than frozen at a kd> prompt, so file_* and con_run work straight away. hard needs confirm=true. |
| additive | Suspend the VM to disk. |
| additive | Resume a suspended VM (same as vm_start). |
| additive | Read or change the VM hardware in the vmx: cpus, memory_mb, nic. Without arguments it reports the current values. A change needs the VM powered off. |
| read | Snapshot tree of a VM plus the current snapshot and stored metadata. retention classes each snapshot kept, temporary, expired or unclassified, and disk gives the free space on the VM drive and the expired snapshots snap_prune would delete. |
| additive | Take a snapshot (memory included while running), record description and kd state, and return the snapshot list. A name that already exists is reported with created=false instead of erroring, and replace=true deletes it and retakes. |
| destructive | Revert to a snapshot: detach kd, revert, start, reattach kd, reopen terminals. The guest comes back running, not frozen at a kd> prompt, so the file_push and con_run that follow a revert work straight away (kd_break freezes it again when that is what you want). |
| destructive | Delete a snapshot (and optionally its children). Needs confirm=true. |
| additive | Mark a snapshot kept (never pruned) or temporary (expires, then snap_prune may delete it). Use it on a snapshot snap_list calls unclassified, or to pin a temporary one you still need. |
| destructive | Delete the expired temporary snapshots of a VM (snap_list names them under disk.prune_candidates) and report the space it got back. Never touches kept, unclassified or current snapshots. Needs confirm=true. |
| additive | Prepare the host side of the kd transport: serial adds the named-pipe COM port to the vmx (VM must be off), net checks the host firewall for kd.exe and repairs it through one UAC prompt. |
| additive | Enable kernel debugging in the guest with bcdedit over SSH (serial or KDNET per kd_transport) and save the KDNET port and key to vms.yaml. Settings that already point at this host are read back, not rewritten. |
| additive | Start kd.exe for the VM and wait until the target connects. |
| additive | Resume the target if needed and stop kd.exe. |
| additive | Break into the running target and wait for the kd> prompt. |
| additive | Resume the target (g). |
| destructive | Run one or more debugger commands at the kd> prompt and return each command's output. A running target (right after kd_attach, or after kd_go) is broken into first, so the first command no longer fails with kd_not_broken. A symbol-heavy command (x, u, ln, lm, a first !extension) or a processor switch can be slow while symbols download: over a network symbol path the first lookup blocks on the server, so set a cache-only path (.sympath cache*C:\symbols) or pre-fetch (.reload /f) once symbols are cached. Raise the timeout, and if one wedges ntdrive interrupts it so the next kd_exec works (at a bugcheck the interrupt keeps the break, but do not kd_detach there: detaching resumes and reboots). A breakpoint that resumes itself (gc) on a hot path NMIs the guest over KDNET: use kd_sample. |
| read | Classify the current bugcheck cheaply: runs .bugcheck (two lines) and returns the code, its arguments and the faulting instruction. Use this before !analyze -v, which takes tens of seconds and prints tens of kilobytes of chkimg noise that is false on a patched kernel. |
| read | Say whether the loaded symbols can actually answer: resolves a type (dt) and a function name (x) and reports each separately, with the current symbol path. A cached PDB that has function names but no type information makes !pool, !process and dt all fail with bad symbols at once, which reads like the commands being wrong rather than the PDB being incomplete. Run this the first time a symbol-dependent command misbehaves. Over a network symbol path the first lookup can block, so attach with symbol_path=cache*C:\symbols on a host with no symbol server. |
| destructive | Capture an OOB-write or fault bugcheck in one call: switch to the processor the bugcheck broke on, find the KTRAP_FRAME on the stack by the faulting RIP (bugcheck Arg3 by default) and .trap it (or .cxr a context_record), then return the unwound call stack (kb), the faulting instruction and the kernel module bases (lm). Needs a bugcheck break (kd_wait_event catches one). It sets a cache-only symbol path first so the symbol-heavy commands cannot wedge on a network symbol server and drop the target mid-capture (cache_symbols=false keeps the current path). Pass fault_rip or context_record for bugchecks that do not carry the fault address in Arg3. Do not kd_detach or kd_go afterwards to leave: both resume and complete the crash, losing the bugcheck. |
| destructive | Break on a symbol, let the target run, and collect the value of one or more expressions at each of the next n hits, then clear the breakpoint. Replaces a manual bp/g/eval round trip per hit. The breakpoint is plain and conditions are evaluated by the daemon, never compiled into the breakpoint, because a conditional breakpoint with gc on a hot function NMIs the guest. |
| read | Wait until the running target stops (bugcheck, breakpoint, ...) or the timeout expires. |
| read | Debugger state: attached (kd.exe alive), state (detached, waiting, running, broken), transport, target info, last event and log path. |
| read | Last bytes of the kd.exe transcript. |
| additive | Open a real-time PTY session (SSH) on the guest and return its session_id. |
| destructive | Type text and/or keys into a session and return at once. Tokens: {enter} {tab} {esc} {ctrl+c} {up}. Use it for a long-running command or one that will stop in the debugger, then term_read or kd_wait_event. |
| read | Read new output (delta), wait for a regex (until), or render the screen (mode=screen). A wait ends with guest_frozen_by_debugger when the target stops at kd>. The PTY echoes what was typed, so an until that the sent command line also contains (echo ===END=== waited on with ===END===) matches that echo and returns before the command ran: pass skip_echo=true, or let term_exec frame the command, which it does by splitting its marker so the echo cannot carry it. |
| destructive | Run one command in the session, wait for it to end (up to timeout) and return only its output and exit code. For a command that will stop in the kernel debugger or drop SSH, use term_send, then kd_wait_event or term_read. |
| destructive | Stop whatever the session's shell is running, keeping the session open. Use it when a command will not end and {ctrl+c} did not help: ctrl+c is a console control event that a program may ignore, and a program that ignores it keeps holding the shell so every later term_exec queues behind it. Each direct child of the shell is killed with its whole tree, so a run that spawned workers leaves no orphan. The shell itself is never killed. It runs over a separate SSH channel, so a busy PTY does not block it. |
| additive | Resize the PTY. |
| additive | Close a session. |
| read | List the usable terminal sessions (their ids are in open, with each one's shell) and the CoView page that mirrors them live in a browser (#<session_id> selects one). Closed and disconnected sessions are only counted, not listed: they pile up as snap_revert and reboot replace the open ones, and dumping hundreds of them once cost a caller its whole token budget. include_closed=true lists them, term_prune forgets them. |
| additive | Forget closed and disconnected terminal sessions (their open successors stay), so the list shows only what is usable. |
| read | Save a PNG of the VM console and return its path (base64 on request). method=vnc reads the console framebuffer without any guest login (con_enable_vnc turns VNC on first). |
| destructive | Type keys into the VM console over VNC, no guest login needed (con_enable_vnc turns VNC on). For a lock or login screen or before the network is up: keys=['{password}', '{enter}'] logs in without the password crossing the wire. |
| destructive | Click the VM console at a framebuffer pixel over VNC, no guest login needed. Read the coordinate off con_screenshot method=vnc (same pixels), for example to pick a user tile on the lock screen, then con_send_keys for the password. |
| additive | Configure Windows automatic logon in the guest so a reboot lands on an unlocked interactive desktop (session 1), which term_* over SSH (session 0) cannot open. enabled=false clears it. needs_reboot: run vm_reboot mode=soft next. |
| destructive | Run a command on the guest's interactive desktop (session 1) and return its output, for GUI or session-bound programs that SSH in session 0 cannot open. It runs through a scheduled task in the logged-on user's session, so the account must be logged in (con_autologon). A missing session fails fast at launch with that hint. shell=powershell carries the text as -EncodedCommand so $vars and quotes need no quoting. detach=true starts it and returns at once, leaving it running. |
| additive | Turn on the console VNC server in the vmx so con_screenshot method=vnc can read the screen without a guest login. Run it with the VM off, then start the VM. |
| destructive | Copy a file, directory or glob from the host into the guest, flush it to the guest's disk and verify it by SHA-256 (over SFTP, or through VMware Tools when SSH is down). Fails with verify_failed when the copy does not match, so a corrupt push cannot read as success. |
| additive | Copy a file from the guest to the host and check its size, failing with verify_failed when the host copy is short. |
| read | Size, last-modified time and is_dir of a guest path, so freshness can be checked without a shell. exists=false when the path is not there. |
| read | List a guest directory (each entry name, size, modified, is_dir), without a shell. |
| destructive | Delete a guest file or directory (recurse for a non-empty directory). deleted=false when it was already absent. |
| read | VM power, debugger state, terminal sessions and last events in one answer. |
| read | Check binaries, config and backend capabilities, then probe every VM: power, guest SSH port and the debugger transport on the host. Run this first: ok is false when the host or any VM has an issue, and every issue names its fix. |
The state model to respect
While the debugger is broken in (
kd_state == broken) the whole guest is frozen. Terminal, file and screenshot tools returnguest_frozen_by_debuggerat once instead of hanging, and a runningterm_execorterm_readwait ends the same way. Runkd gofirst.snap revertdetaches the debugger andvm rebootkeeps kd.exe waiting for the reconnect. Both drop terminal sessions, then reattach and reopen them unless you pass--no-reattach-kdor--no-reopen-term. The reopened sessions have new ids, listed in the result asterm[].new.After a reconnect the old terminal session id is kept and points to its successor.
The full state model and the procedures are in SKILL.md.
Encrypted VMs
Set encryption_password_env (the name of an environment variable, preferred) or
encryption_password (inline, acceptable because vms.yaml is git-ignored) and every operation
that opens the vmx passes -vp to vmrun. Power, snapshot list, revert, delete, screenshot, guest IP
and file copy all work on an encrypted VM, including the partial encryption that a Windows 11 vTPM
requires. The one rough edge: vmrun refuses a live snapshot, or a memory snapshot delete, of a
running encrypted VM. snap_take and snap_delete then answer with
error.reason=encrypted_live_snapshot, and --allow-suspend handles it (suspend, snapshot or
delete, resume: terminal sessions are dropped and the debugger is reattached). A snapshot of a
powered-off VM never needs it. When the resume fails the call fails with the snapshot recorded
and the facts in the error, and vm_start --discard-saved-state boots fresh when the vmx still
names a saved state Workstation cannot restore. What vmrun accepts is recorded in AGENTS.md, Things that bit us.
Safety
The daemon binds only to
127.0.0.1and checks a per-daemon token on every request.Secrets (guest password, KDNET key, VM encryption password, daemon token) live only in
vms.yaml, environment variables, and the daemon state file. They never appear in tool arguments, results or logs, and the audit log masks them.The guest's SSH host key is pinned per VM on first use under
%LOCALAPPDATA%\ntdrive\hostkeys. A different key later is refused before the password is sent. Delete that file after reinstalling a guest.The CoView URL carries a view token, a second secret that only lists terminal sessions and opens their streams. That is still a shell in the guest, so treat it like a password and do not paste it into chat or tickets. Terminal transcripts under the log directory record everything typed into a session, by the agent or by a person, so passwords typed interactively land there too.
kd_execexecutes any debugger command, and kd's.shellruns commands on the host. The policy file can setkd_exec: denyfor agents that should not have that.Destructive actions (
snap_delete,vm_stop mode=hardorkill,vm_reboot mode=hard,vm_upgrade,snap_prune) requireconfirm=trueand go through a policy gate you can tune inpolicy.yaml.
Documentation map
File | Read it if you are |
| a human setting up or using ntdrive |
| an agent using the tools. State model, standard procedures, and moves to avoid |
| any coding agent changing this repo. Conventions, commands and where things live |
| Claude Code. Points at |
| anyone who wants the full requirements and design rationale |
| someone editing |
| someone tuning the allow, confirm, deny level per tool |
| host and guest setup, see Setup above |
SKILL.md is written for the agent. MCP clients that read skill files pick it up, and any agent can
be pointed at it. It covers the state model, the setup and debug-loop and BSOD-recovery procedures,
and the forbidden moves (such as touching the terminal while the debugger is broken in).
Development
uv sync
uv run --no-sync pre-commit run --all-files # ruff format, ruff check, mypy, prettier, ASCII check
uv run --no-sync pytest -q # unit tests, all fakes, no real VM neededBoth must pass before a commit, and CI runs exactly these two on windows-latest. --no-sync
because a sync rewrites ntdrive-mcp.exe, which fails while an MCP client such as Claude Code
holds it. Conventions, the map of the code and what to update when a tool changes are in
AGENTS.md. Ruff is the only Python formatter and linter, mypy is strict for the whole package,
and Prettier runs on the CoView web assets only. A pre-commit hook rejects non-ASCII characters
and semicolons in Markdown prose, which keeps every document English and plain. Nothing in the
test suite needs a VM: the one test that touches the real Windows firewall runs only with
NTDRIVE_LIVE_TESTS=1, and the live check is ntdrive verify (Setup, step 3).
The MCP face can be poked by hand with the inspector: npx @modelcontextprotocol/inspector ntdrive-mcp (from a clone, npx @modelcontextprotocol/inspector uv run ntdrive-mcp) opens a page
that lists the tools with their annotations and calls them.
Only the VMware backend is implemented. Hyper-V and VirtualBox sit behind the same
HypervisorAdapter interface and are planned for a later version.
Layout
src/ntdrive/
config.py vms.yaml and policy.yaml models
errors.py NtDriveError with code, hint and backend reason tags
hostproc.py runs vmrun and the PowerShell helpers without a console window
paths.py host path helpers shared by the CLI, SDK and file tools
core/ registry, state store, policy gate, audit log, orchestrator, service
core/tools/ tool handlers: vm, snap, kd, term, console, file, sys
hypervisor/ HypervisorAdapter, the VMware (vmrun) adapter, vmx file access
kd/ KdSession around kd.exe, host firewall repair for KDNET
term/ terminal transports, sessions, reconnect and successor ids, key tokens
screen/ VNC framebuffer capture for con_screenshot method=vnc
daemon/ ntdrived HTTP and WebSocket app, lifecycle, client, CoView page
mcp/ sdk/ generated front doors
cli/ generated tool commands plus setup, verify, term attach and the daemon group
scripts/ host and guest setup with their .cmd launchers, a guest probe, the ASCII hook,
the README table generator, the wordmark generator
assets/ the README wordmark, drawn by scripts/make_logo.py
tests/ unit tests with fake vmrun, fake kd.exe and fake terminal channelsLicense
MIT. See LICENSE.
Available Tools
33 toolscon_screenshotA
Save a PNG of the VM console and return its path (base64 on request).
Inputs: vm (required), base64.
file_pullA
Copy a file from the guest to the host.
Inputs: vm (required), local (required), remote (required).
file_pushA
Copy a file, directory or glob from the host into the guest and verify it.
Inputs: vm (required), local (required), remote (required), verify.
kd_attachB
Start kd.exe for the VM and (by default) wait until the target connects.
Inputs: vm (required), key, port, timeout, +2 more.
kd_breakB
Break into the running target and wait for the kd> prompt.
Inputs: vm (required), timeout.
kd_detachA
Resume the target if needed and stop kd.exe.
Inputs: vm (required), force.
kd_execB
Run one or more debugger commands at the kd> prompt and return each command's output.
Inputs: vm (required), cmd, cmds, timeout, +1 more.
kd_goD
Resume the target (g).
Inputs: vm (required).
kd_log_tailC
Last bytes of the kd.exe transcript.
Inputs: vm (required), bytes.
kd_setup_guestA
Enable kernel debugging in the guest with bcdedit over SSH (serial or KDNET per kd_transport) and store the KDNET port and key in vms.yaml.
Inputs: vm (required), key, port.
kd_setup_hostB
Prepare the host side of the kd transport: serial adds the named-pipe COM port to the vmx (VM must be off), net reports the one-time firewall step.
Inputs: vm (required).
kd_stateC
Debugger state, transport, target info, last event and log path.
Inputs: vm (required).
kd_wait_eventC
Wait until the running target stops (bugcheck, breakpoint, ...) or the timeout expires.
Inputs: vm (required), timeout.
snap_deleteA
Delete a snapshot (and optionally its children). Needs confirm=true.
Inputs: vm (required), name (required), confirm, children, +1 more.
snap_listB
Snapshot tree of a VM plus the current snapshot and stored metadata.
Inputs: vm (required).
snap_revertA
Revert to a snapshot: detach kd, revert, start, reattach kd, reopen terminals.
Inputs: vm (required), name (required), start, timeout, +2 more.
snap_takeA
Take a snapshot (memory included while running) and record description and kd state.
Inputs: vm (required), name (required), description, allow_suspend.
sys_healthA
Check binaries, config and backend capabilities, then probe every VM: power, guest SSH port and the debugger transport on the host. Run this first.
Inputs: no named parameters.
sys_stateA
VM power, debugger state, terminal sessions and last events in one answer.
Inputs: vm.
term_closeC
Close a session.
Inputs: session_id (required).
term_execB
Run one command in the session and return only its output and exit code.
Inputs: cmd (required), session_id (required), timeout, max_bytes.
term_listC
List terminal sessions and their state.
Inputs: vm.
term_openB
Open a real-time PTY session (SSH) on the guest and return its session_id.
Inputs: vm (required), cols, rows, shell, +1 more.
term_readC
Read new output (delta), wait for a regex (until), or render the screen (mode=screen).
Inputs: session_id (required), mode, clean, until, +3 more.
term_resizeD
Resize the PTY.
Inputs: cols (required), rows (required), session_id (required).
term_sendB
Type text and/or a burst of keys into a session. Tokens: {enter} {tab} {esc} {ctrl+c} {up}.
Inputs: session_id (required), keys, text, enter.
vm_listA
List registered VMs with power, debugger and terminal state.
Inputs: no named parameters.
vm_rebootA
Reboot the guest (soft, hard or from the debugger) and bring kd and terminals back.
Inputs: vm (required), mode, confirm, timeout, +2 more.
vm_resumeB
Resume a suspended VM (same as vm_start).
Inputs: vm (required).
vm_startC
Power on (or resume) a VM without the GUI by default.
Inputs: vm (required), gui.
vm_stateB
Power, debugger and terminal state of one VM.
Inputs: vm (required).
vm_stopA
Shut the guest down (soft) or cut power (hard, needs confirm=true).
Inputs: vm (required), mode, confirm.
vm_suspendB
Suspend the VM to disk.
Inputs: vm (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 33 tools
Most tools are clearly separated by domain prefix (kd_, con_, file_, term_, vm_, snap_, sys_), and within each domain the action is distinct. The only mild ambiguity is vm_resume vs vm_start (explicitly noted as same) and kd_go vs kd_detach (both resume the target, but one detaches kd.exe).
The naming follows a consistent verb_noun pattern with domain prefixes (kd_*, term_*, vm_*, snap_*, sys_*). Minor deviations: kd_setup_host/kd_setup_guest use setup as a verb, and con_screenshot is the only con_* tool, but overall the pattern is predictable.
33 tools is on the heavy side, but the server covers a broad domain (VM lifecycle, terminal sessions, kernel debugging, snapshots, file transfer, health checks). Each tool has a specific purpose, though some consolidation (e.g., vm_state vs vm_list vs sys_state) could reduce the count.
The tool surface is remarkably complete for the stated purpose: VM lifecycle (start/stop/reboot/suspend/resume), snapshots (list/take/revert/delete), terminal sessions (open/send/read/exec/resize/list/close), kernel debugging (setup/attach/break/go/exec/wait/state/log), file transfer (push/pull), and system health/state. No obvious dead ends.
Related MCP Connectors
Connect ChatGPT, Claude, Cursor or VS Code to your Windows PC: files, terminal, browser, apps.
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Use your Mac, Windows or Linux computer from ChatGPT, Claude or Codex: files, commands, documents.
Provides capabilities that let LLM agents perform a range of infrastructure management tasks.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn MCP server that gives an LLM full control over a VMware-hosted Windows VM: lifecycle, snapshots, remote execution, file transfer, and kernel debugging.MIT
- FlicenseNot gradedqualityBmaintenanceEnables management of VMware Workstation virtual machines, including lifecycle and snapshot operations, through MCP tools accessible by AI assistants.-
- AlicenseNot gradedqualityDmaintenanceMCP server for building and driving VMware Workstation VMs from an AI agent, enabling unattended OS installs, guest command execution, file transfer, screenshots, and fleet management.48 npm4MIT
- FlicenseNot gradedqualityBmaintenanceEnables controlling VMware Workstation Pro virtual machines, including listing, power management, snapshots, cloning, guest operations, networking, and screenshots.-