dosbox-x-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., "@dosbox-x-mcpFind the DOS guest and list loaded programs."
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.
dosbox-x-mcp
An MCP server that drives a DOSBox-X guest without touching the desktop: no focus stealing, no synthetic host keystrokes, no pointer moves. A model can run a DOS program start to finish while you keep working in front of it.
It began as the capture workflow for one reverse-engineering project and is now a general aid. Given a running DOS guest it can find that guest with no prior knowledge at all, tell you what programs are loaded and where, read and patch any segment, sample values over time, read the screen out of video memory, drive the emulator's own recorders, and watch code as it runs — without a debugger and without changing a byte on disk.
What it can do
Find a guest | Locate a DOS guest from BIOS invariants alone — no profile, no marker, nothing known in advance. Walk the DOS memory chain and name every loaded program, its segment and the path it came from. |
Address any segment | Another program in a launcher chain, a TSR, an overlay, the interrupt vector table, EMS pages. Read, patch, dump, search. |
Drive it | Keys into the BIOS keyboard ring. Clicks into the game's own post-INT-33h mouse words. Neither goes near the host's input queue. |
See it | Read the framebuffer straight out of the emulator's video memory: exact indices, no window, no scaling, no cost to the guest — confirmed against a reference frame. Or photograph the window when you must. |
Measure it | Block on a memory condition, or sample a watch list at up to 200 Hz into a TSV — every column from one snapshot, so they cannot drift apart. Watch video memory for every distinct frame and when it appeared. |
Record it | The emulator's own OPL, MIDI and WAVE capture, fired without focus and without the host mapper. |
Instrument it | Redirect a live |
Related MCP server: re-winedbg
How it works
Nothing here involves the host's input queue or screen.
Guest discovery. Every DOS guest has the BIOS keyboard ring's extent words
0x001E/0x003Eat0040:0080, a head and tail inside that extent, and a liveINT 21hvector. Finding that triple in the emulator's memory finds guest physical zero, and from there every segment is addressable. This needs no profile, which is what breaks the chicken and egg a new project starts inside.Keys are appended to the guest's BIOS keyboard ring exactly as a real keyboard interrupt would.
Clicks are written into the words the game's own INT 33h handler fills in — the state the game actually reads. They are rewritten continuously for a moment, because the game's handler keeps overwriting them and a single write loses that race.
Frames come out of video memory. DOSBox stores a chain-4 page the way the hardware does — CPU offset
oat linear4 * (o & ~3) + (o & 3)— so taking every sixteenth group of four turns a region back into a screen. Finding which bytes are the screen takes a reference frame: an emulator's memory is full of picture-like data that is not the picture, so a page is either confirmed byte for byte against a reference or reported as unconfirmed. See Reading the screen.Traces patch a near
CALLto point at a run of zero padding. The cave calls the displaced target, preserves its flags, copies what you asked for into slots after its own code, and returns. The emulator dies with the run and takes the patch with it.
Install
pip install "dosbox-x-mcp[all] @ git+https://github.com/md0-code/dosbox-x-mcp"The extras are all optional, and named for what they buy:
Extra | For |
| Pillow, for photographing the emulator's window |
| numpy, for a quicker chain-4 de-interleave (there is a pure-Python fallback) |
| python-xlib, for listing and photographing windows on Linux |
Register it with your MCP client. For Claude Code, an .mcp.json at your
project root:
{
"mcpServers": {
"dosbox": {
"command": "python",
"args": ["-m", "dosbox_mcp.server"],
"env": {
"DOSBOX_MCP_PROFILE_DIR": "dosbox-x/profiles",
"DOSBOX_MCP_EXECUTABLE": "dosbox-x/dosbox-x.exe"
}
}
}
}Variable | Meaning |
| where profiles live (default: the package's |
| default profile name; |
| the |
| where a relative output or reference path is taken from |
Every path a tool reads or writes follows one rule: absolute is taken as given,
relative is under DOSBOX_MCP_OUTPUT_DIR. Tools return the resolved path, so
there is never any doubt where a file went.
Tools
Tool | What it does |
| what this host can and cannot do |
| list profiles; show one profile's named offsets |
| start DOSBox-X and wait until the guest is drivable |
| attach to an already-running emulator by pid |
| attached sessions, plus any other DOSBox-X windows |
| end a session |
| find the guest and list every loaded program — no profile needed |
| find a byte pattern, reported as guest |
| type into the BIOS keyboard ring |
| click through the game's own mouse words |
| hold buttons down, optionally until a memory test passes |
| read the data segment, or any segment |
| patch the data segment, or any segment |
| write a whole 64 KiB segment to a file |
| block until a memory field satisfies a test |
| sample a watch list over time into a TSV |
| return the current frame as an image to look at |
| save an exact frame, from video memory or the window |
| read the page out of video memory |
| every distinct frame in a stretch of time, with timings |
| fire one of DOSBox-X's own menu items |
| record OPL, MIDI or WAVE output to a file |
| find zero-filled runs big enough for a trace |
| redirect a live |
| read what a trace has recorded |
| restore the call site and blank the cave |
Anywhere an offset is accepted, a profile symbol name works too — treasury
rather than 0x634A.
Starting on a game nobody has profiled
A profile is not a precondition. This is the whole first session:
dosbox_launch(config="game.conf", profile="none") → pid
dosbox_find_guest()
→ 640 KiB, INT 21h live, and:
JP2D load segment 2456 1.1 MB C:\JP\JP2D.EXE
JP load segment 08A1 64 KB C:\JP\JP.EXE
COMMAND load segment 0801 16 KB C:\COMMAND.COM
dosbox_search_memory(text="sprites.dbt") → 2456:027A
dosbox_dump_segment(path="jp2d.bin", segment="0x2456")That dump is the profile's marker: pick 50–100 bytes of it that will not change between runs, add two or three cheap checks, and every DS-relative tool starts working by name.
Reading the screen
dosbox_read_framebuffer returns the palette indices the guest wrote, at the
instant of the read. It is exact, it cannot catch a half-drawn frame, and it
costs the guest nothing — which is why it is the path to prefer for anything
being measured.
It needs a reference frame, and it says so rather than guessing. Locating
the page means finding 64,000 bytes among hundreds of megabytes, and coherence
alone is not enough: measured against a live game, a blind scan returned a page
scoring 0.999 that was a decoded sprite bank, not the screen. So pass
reference= a 64,000-byte file holding what is on screen right now, and the
page is confirmed byte for byte:
dosbox_read_framebuffer(reference="credits_logo.bin", stem="shots/logo")
→ page_offset 16, confirmed true, pages [16, 64016, 128016, 192016]Where a reference comes from:
A port under development has one for free — its own render of the same screen. That is also the comparison worth making: if the two agree byte for byte, the port's renderer is right.
Any earlier confirmed capture of the same screen re-confirms it.
A window photograph, when the profile carries a palette — this is automatic, and needs no argument.
Once confirmed the location is cached, so every later read and every frame of
dosbox_watch_frames is free. allow_unconfirmed=true takes the blind scan's
guess for anyone who wants to check it themselves.
The page is not necessarily where you would guess: it begins where the CRTC start address puts it, which is a four-byte boundary and nothing coarser. The one measured live sat at offset 16.
Profiles
A profile is one JSON file describing a game: how to recognise its data
segment, where it keeps its mouse state, its screen size and palette, named
offsets, watch sets and known caves.
profiles/example.json is an annotated template; the
OpenJP repository has real ones written
against a shipped 1993 game.
{
"name": "example",
"ds_segment": "0x1234",
"marker": { "bytes": "6578616d706c652e64617400", "offset": "0x0100" },
"checks": [ { "kind": "cstring_via_pointer", "pointer": "0x0200", "value": "game" } ],
"mouse": { "buttons": "0x00B2", "position": "0x00B6" },
"screen": { "width": 320, "height": 200 },
"symbols": { "lives": { "offset": "0x1234", "size": 1, "description": "Lives left." } },
"watch_sets": { "player": ["lives", "score", "level"] }
}The marker can be inlined as bytes hex or sliced out of a reference dump
(source_dump + offset + length). Checks available: cstring_via_pointer,
max, max_range, equals — enough to make a false positive vanishingly
unlikely, which matters because the alternative is patching a random
allocation.
Platforms
Windows | Linux | macOS | |
Guest memory, segments, search, keys, clicks | yes | yes | no backend |
Framebuffer, sampling, traces | yes | yes | no backend |
Window list, capture, resize | yes | with X11 | — |
Emulator menu commands | yes | no — use an isolated display | — |
Offscreen display | — | with | — |
dosbox_capabilities reports all of this for the running host, so ask rather
than guess.
On Linux, kernel.yama.ptrace_scope gates access to another process's
memory exactly as integrity level does on Windows:
Value | Effect |
| any same-uid process — |
| descendants only — |
|
|
| no attach at all |
Under the common default, launch rather than attach. The server reads the
sysctl and says so rather than surfacing a bare EPERM.
Linux has no way to photograph an occluded window without a compositing
manager, and Wayland has no cross-client capture at all. The answer is not to
emulate PrintWindow but to remove the constraint it exists to satisfy:
dosbox_launch(isolated=true) puts the emulator on its own Xvfb display,
where there is no desktop to protect and the emulator's own keyboard shortcuts
are usable without taking a keystroke from anyone.
macOS would need task_for_pid, and therefore either root or a signed, entitled
binary. There is no backend for it.
Caveats
The emulator must be reachable: same integrity level on Windows, an agreeable
ptrace_scopeon Linux.One emulator per profile at a time. Two guests running the same game make the segment scan ambiguous, and the server refuses rather than guessing.
dosbox_capture_screenwithsource="window"resizes the emulator window to get an exact integer scale. That is the one visible effect this server has on the desktop — and the reason to prefersource="vram", which is also faster and cannot catch a half-drawn frame.Photographing the window costs the guest real time: a filming loop stretches guest-visible phases by roughly 1.6x. Use the framebuffer for anything quantitative.
A click can advance two "click to continue" pages in some games; send a key when paging through lists.
Writes into a running game are not undoable, and the game may recompute a field right after you set it — patch at a moment where it will be read before it is recomputed.
Trace memory captures are read through whatever
DSholds when the cave runs. For a game with one data segment that is exactly right; for a routine that switchesDS, capturedsalongside and check it.Only chain-4 linear mode 13h is read out of video memory. Anything else needs
source="window".A blind framebuffer scan is a hint, not an answer, and is reported as unconfirmed. Give a reference frame.
Development
pip install -e ".[dev,all]"
pytestThe suite is entirely offline: a DOS guest, a memory chain, a chain-4
framebuffer and a traceable code segment are all built in a bytearray, so it
runs on any platform with no emulator and no game. What it does not cover is
the two system calls at the very bottom — reading and writing another process —
and the window.
Licence
MIT.
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
- FlicenseAqualityDmaintenanceEnables programmatic control of the mGBA emulator for Game Boy, Game Boy Color, and Game Boy Advance games, including screenshot capture, memory reading, sprite data dumping, and custom Lua script execution for automated testing and game analysis.63
- AlicenseNot gradedqualityCmaintenanceEnables headless debugging of Windows executables from Linux/macOS hosts by orchestrating winedbg's gdbserver and a GDB client, exposing 19 tools for launch, attach, breakpoints, stepping, register/memory access, and session lifecycle.MIT
- AlicenseNot gradedqualityCmaintenanceBridges AI agents to a DOSBox emulator, enabling control of DOS programs via MCP tools for typing, screen reading, video capture, Lua scripting, and memory access.1GPL 2.0
- FlicenseAqualityBmaintenanceEnables an MCP client to observe and control a text-mode DOS system via a Python bridge, supporting keyboard input and screen capture.8
Related MCP Connectors
Eyes and hands on real Windows PCs — observe, click, type via Glasswarp API.
Live browser debugging for AI assistants — DOM, console, network via MCP.
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
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/md0-code/dosbox-x-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server