dosbox-x-mcp
dosbox-x-mcp is an MCP server that drives a DOSBox-X guest through memory and BIOS-level input without touching the host desktop.
Session management: launch, attach to, list, and quit DOSBox-X processes; wait until the guest is drivable; run isolated on Xvfb on Linux.
Guest discovery: find a DOS guest from BIOS invariants, walk the DOS memory chain, list loaded programs and their segments/paths with no profile.
Memory access: read/write any guest segment (data, code, IVT, EMS), use symbols or raw offsets, dump whole segments, search for byte patterns/text.
Input without focus: send keys through the BIOS keyboard ring, hold authentic make/break keys via a companion hook, click and hold mouse buttons through INT 33h guest state.
Screen capture: read exact framebuffer frames from video memory (with reference confirmation), photograph the window when needed, save frames as PNG/RGB, watch and time distinct frames.
Measurement: sample memory fields up to 200 Hz into TSV, wait for memory conditions, correlate memory series with visual series, scan changed fields across experiment/control runs.
Code tracing: install/read/remove live CALL traces through zero-filled code caves to record registers and memory without altering disk.
Emulator commands: fire DOSBox-X menu actions such as OPL/MIDI/WAVE/video recording, screenshot, save/load state (Windows).
Experiments: run bounded actions with observe, isolated save-state trials, probe/qualify controls, compare frames, estimate camera paths, build world mosaics, infer movement constraints, maintain an evidence notebook, and create/verify/reproduce immutable experiment bundles.
Profiles: load game profiles with named offsets, watch sets, and markers; report host capabilities so callers know what is possible on the current platform.
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., "@dosbox-x-mcp Find 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 | Menu keys through the BIOS ring; authentic simultaneous make/break holds through the companion DOSBox-X hook; clicks through the game's post-INT-33h mouse words. None 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. |
Experiment | Compare exact frames, compensate camera motion, extract and track neutral changed regions, branch actions from one isolated save state, probe controls, scan unknown memory, and retain evidence separately from profiles. |
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: dosbox-mcp
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.Menu keys are appended to the guest's BIOS keyboard ring.
Held keys enter DOSBox-X's own
KEYBOARD_AddKeypath through a small, versioned companion hook. This produces real make/break scan codes for IRQ1 games, supports simultaneous keys, and never activates the emulator window.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.
Captures can leave guest memory unchanged while creating parent directories
and creating or overwriting host files. DOSBOX_MCP_OUTPUT_DIR is the base for relative
paths, not an enforced boundary; absolute paths are supported. Use distinct
filenames to retain separate observations.
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 |
| set authentic make/break state with automatic lease release |
| hold simultaneous authentic keys for a bounded duration |
| schedule ordered input, capture, memory and trace events against emulator counters |
| report protocol-v2 capabilities, session identity and guest-time counters |
| 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 |
| exact differences, changed regions and neutral tracks across saved indexed frames |
| one bounded action plus its visual and memory consequences |
| restore one isolated save-state baseline before each labelled trial |
| rank repeated authentic held-key responses above idle motion |
| rank camera, local-visual, memory and transition evidence behind automatic quality gates |
| evaluate strict or exploratory experiment-quality gates without running a trial |
| build a multi-frame consensus camera path with ambiguity, outliers and exact loop closure |
| stitch indexed frames in measured camera coordinates, retaining conflicts |
| find repeated requested-direction stalls without calling them collisions |
| rank 1-, 2- or 4-byte fields across experiment/control snapshots |
| correlate a visual numeric series with candidate memory series |
| persist observed, inferred and confirmed claims outside profiles |
| 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 |
| attribute bounded guest-memory writes to exact runtime CS:IP writers |
| capture acknowledged raw VGA planes/pages and same-frame DAC state |
| exactly decode a stored Mode X, planar EGA/VGA, text or packed VESA snapshot |
| finalise raw and derived evidence into an immutable, hashed bundle |
| verify every bundle artifact and manifest hash offline |
| reproduce stored comparisons, camera paths, mosaics, tiles and constraints offline |
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.
Perception and controlled experiments
Perception tools report measurements, not game meanings. A region is an exact connected set of changed pixels; a track is a deterministic association between regions in successive transitions. Neither is silently called a player, enemy, wall or sprite. Memory offsets are likewise candidates until independently confirmed.
Compare two saved indexed frames:
dosbox_compare_frames(
frames=["trial_before.bin", "trial_after.bin"],
width=320, height=200, bridge_radius=1, min_changed_pixels=3)
→ 428 changed pixels; region 1 bbox [115,92,23,27]Pass a longer sequence to obtain stable track IDs and measured displacement:
dosbox_compare_frames(
frames=["walk_000.bin", "walk_001.bin", "walk_002.bin"],
bridge_radius=2)
→ track 3: dx +7, dy 0, age 2, confidence 0.91For scrolling scenes, enable bounded global translation and screen-position
evidence. The result includes the best (dx, dy), match and ambiguity scores,
translation-compensated residual regions, and neutral region categories:
dosbox_compare_frames(
frames=["walk_000.bin", "walk_001.bin", "walk_002.bin"],
detect_global_motion=true, infer_screen_layout=true,
max_translation_x=16, max_translation_y=16)Run an idle control or a bounded action and collect both evidence streams in one round trip:
dosbox_act_and_observe(
action={"kind":"idle"}, duration_seconds=1.0,
memory_fields=[{"label":"clock","offset":"tick_counter","size":4}],
memory_range={"offset":"0x6000","length":1024,"value_size":2},
stem="experiments/idle")
dosbox_act_and_observe(
action={"kind":"hold_keys","keys":["right"],"duration_seconds":0.35},
memory_fields=[{"label":"candidate_x","offset":"0x19A4","size":2}],
stem="experiments/right")kind="keys" appends BIOS key events. It does not hold a physical key or
manufacture make/break state by repeatedly filling the BIOS queue. Games that
read IRQ1, port 0x60, or their own held-key table may ignore it; the tool says
so in every result. Use kind="hold_keys" with a
companion-enabled DOSBox-X build for those games.
Run independent trials from one baseline by launching with a dedicated state directory. The server reserves slot 100 there and restores it before every trial and once more before returning, without touching personal save slots:
dosbox_launch(config="game.conf", profile="none",
state_directory="experiments/state")
dosbox_try_actions(
trials=[
{"label":"idle","action":{"kind":"idle"}},
{"label":"right","action":{"kind":"hold_keys","keys":["right"],
"duration_seconds":0.35}}
], duration_seconds=0.75, stem="experiments/branch")dosbox_probe_controls automates repeated idle and held-key branches and ranks
repeatable visual responses. Rankings remain neutral evidence: they do not name
a track as the player.
Compare independent branches or repeated trials with an idle control and an opposite action:
dosbox_scan_changes(
baseline="baseline.bin", experiment="right.bin",
control="idle.bin", opposite="left.bin",
value_size=2, relation="increased", aligned=true)
→ DS:19A4 increased during RIGHT, stayed stable while idle,
and changed in the opposite direction during LEFTFinally, rank sampled candidate fields against a measured track coordinate:
dosbox_correlate_memory(
visual_series=[115,120,126,132],
memory_series={"DS:19A4":[1840,1920,2016,2112]})
→ raw/difference correlation, direction, and simple fixed-scale evidenceStore conclusions in a separate evidence notebook instead of turning a hypothesis into a profile fact:
dosbox_evidence_notebook(
operation="upsert", path="experiments/evidence.json",
subject="memory:DS:19A4", status="inferred", label="horizontal state",
confidence=0.92,
evidence=["increased in repeated RIGHT branches", "stable in idle branches"])Notebook revisions retain their history and use the explicit statuses
observed, inferred, and confirmed.
For auditable work, finalise inputs, timelines, observations, derivations and gate records into an experiment bundle. Bundles are immutable, hash every artifact, can be verified or reproduced offline, and allow confirmed notebook entries to cite exact artifact IDs.
After camera translation is understood, stitch frames into an indexed world mosaic. Supply measured translations or let the tool run the same bounded translation detector. It writes the raw mosaic, a PNG, and a coverage map; overlap conflicts remain counted rather than being silently repaired:
dosbox_build_world_mosaic(
frames=["flight_000.bin", "flight_001.bin", "flight_002.bin"],
translations=[{"dx":-4,"dy":0},{"dx":-4,"dy":0}],
stem="experiments/world", infer_tiles=true,
candidate_tile_sizes=[4,8,16,32])Optional tile inference ranks exact repeated block grids, then writes a raw tile catalogue and integer tile map. Those IDs carry no terrain or collision meaning until supported by separate evidence.
Repeated position samples can identify places where requested motion made no
forward progress. The result deliberately calls these movement_constraints,
not walls or collisions:
dosbox_infer_movement_constraints(
trials=[
{"label":"right_a","direction":{"x":1,"y":0},
"positions":[{"x":10,"y":20},{"x":11,"y":20},{"x":11,"y":20},
{"x":11,"y":20},{"x":11,"y":20}]},
{"label":"right_b","direction":{"x":1,"y":0},
"positions":[{"x":9,"y":20},{"x":11,"y":20},{"x":11,"y":20},
{"x":11,"y":20},{"x":11,"y":20}]}
])Frame processing is dependency-free and practical for 320×200 indexed modes. Large whole-screen transitions reset associations instead of creating hundreds of long-lived tracks; crossings and temporary disappearances reduce confidence.
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.Indexed mode 13h pages stored in DOSBox-X's chain-4 or linear backing layout are 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.
Available Tools
26 toolsdosbox_attachA
Attach to a DOSBox-X process that is already running, by process id, and locate the game's data segment inside it. Use dosbox_sessions to find candidate process ids.
Inputs: pid (required), profile. Returns: object
dosbox_capabilitiesARead-only
What this host can do, and what it cannot. Guest memory works everywhere there is a backend; window capture and the emulator's own menu commands do not. Call this when a tool refuses, or before planning a session that depends on frames or recordings.
Inputs: no named parameters. Returns: object
dosbox_capture_screenA
Capture the current frame exactly and save it: .rgb (one byte per pixel), .png, and with with_segment also _ds.bin, the data segment from the same instant -- which is what makes a captured frame replayable as a fixture.
source='vram' reads the frame out of the emulator's video memory, which is exact, cheap, cannot catch a half-drawn frame and does not slow the guest. source='window' photographs the window instead, resizing it to an integer multiple of the guest resolution; that is the one visible effect this server has on the desktop, and it costs the guest real time. 'auto' takes video memory when the page has been CONFIRMED against a reference and falls back to the window otherwise, so it never quietly saves the wrong 64,000 bytes.
Inputs: stem (required), pid, exact, scale, +4 more. Returns: object
dosbox_clickA
Click at a guest screen coordinate by writing the game's own mouse state, the words its INT 33h handler normally fills in. The host pointer never moves. The state is rewritten continuously for duration because the game's handler keeps overwriting it; a single write usually loses that race. Beware: on 'click to continue' pages one click can advance two pages -- send a key instead when paging through lists.
Inputs: x (required), y (required), pid, button, +1 more. Returns: object
dosbox_dump_segmentARead-only
Write a whole 64 KiB guest segment to a file -- the state at one instant, for later analysis or as a test fixture. With no segment this is the profile's data segment; with segment=0x1234 it is any segment in the guest, which is how the marker for a new profile is captured.
Inputs: path (required), pid, segment. Returns: object
dosbox_emulator_commandA
Fire one of DOSBox-X's own menu items without focus and without a keystroke: record_opl, record_midi, record_wave, record_video, screenshot, save_state, load_state. Firing a record action again stops that recording. Windows only -- DOSBox-X draws its own menu elsewhere; see dosbox_capabilities.
Inputs: action (required), pid. Returns: object
dosbox_find_caveARead-only
Find runs of zero in a guest code segment long enough to hold a trace cave. Padding between routines is the usual supply. Longest first; pass one of these offsets to dosbox_install_trace.
Inputs: segment (required), pid, bytes_needed. Returns: object
dosbox_find_guestARead-only
Find the DOS guest inside an emulator process and report what is loaded in it -- with no profile and no prior knowledge. This is the way in for a game nobody has described yet: it locates guest physical zero from the BIOS data area, then walks the DOS memory chain and names every loaded program, its load segment and the path it was started from. Those segments are what dosbox_read_memory(segment=...) and dosbox_search_memory take, and what a new profile is written from.
Inputs: pid. Returns: object
dosbox_hold_buttonsA
Hold mouse buttons down for a stretch of time by writing the guest's button word continuously -- the way to drive anything that reads a held button, such as an action sequence that only advances while a button is down. Sparse writes lose to the game's own interrupt handler, so this busy-writes. Give stop_when to release as soon as a memory field says the sequence is over, instead of holding for the full time.
Inputs: seconds (required), pid, buttons, poll_seconds, +5 more. Returns: object
dosbox_install_traceADestructive
Watch a running guest's code without a debugger. Redirects one near CALL in the live code segment through a zero-filled cave that calls the displaced target, preserves its flags, and records the registers and memory words you name -- then read them with dosbox_read_trace while the guest runs at full speed. Nothing on disk changes; the patch dies with the emulator. captures take: a register (ax bx cx dx si di bp sp), a segment register (ds es ss cs), flags, or memory -- word:0x9D10, byte:DS:0x7A0E, word:ES:0x0004. Memory is read through whatever DS holds when the cave runs; capture ds too if the routine changes it. Give expect_hex (the three bytes the call site should hold) so the trace refuses rather than landing on a moved target.
Inputs: call_at (required), cave_at (required), segment (required), captures (required), +3 more. Returns: object
dosbox_launchA
Start DOSBox-X with a config file and attach to it. Returns the process id every other tool takes. The config's [autoexec] section decides what runs; an AUTOTYPE line there is the reliable way past intro screens. Waits until the guest is findable, so a successful call means it is really up and drivable. On Linux, isolated=true puts the emulator on its own Xvfb display -- entirely off the user's desktop, and the only way the emulator's own keyboard shortcuts can be used without taking a keystroke from whatever the user is doing.
Inputs: config (required), profile, isolated, executable, +2 more. Returns: object
dosbox_profilesARead-only
List the game profiles this server knows and, for one of them, every named memory offset it exposes. A profile says how to recognise the game's data segment, where the game keeps its mouse state, and what its screen looks like. Call this first: the symbol names it returns can be used anywhere an offset is accepted.
Inputs: profile. Returns: object
dosbox_quitADestructive
Close a DOSBox-X session. By default only sessions this server launched may be killed; pass force to end one it merely attached to. The guest is not asked to save anything first.
Inputs: pid, force. Returns: object
dosbox_read_framebufferARead-only
Read the guest's screen straight out of the emulator's video memory: the palette indices the guest wrote, at the instant of the read, with no window, no scaling and no palette round trip. Prefer this over dosbox_capture_screen for anything quantitative -- photographing the window costs the guest real time and can catch a half-drawn frame.
FINDING THE PAGE NEEDS A REFERENCE. Give reference= a 64,000-byte frame of what is on screen and the page is confirmed byte for byte; without one this refuses, because a blind scan finds the most picture-like page in memory and on a real guest that is often a sprite bank or an offscreen buffer. Once confirmed the location is cached and every later read is free. allow_unconfirmed=true takes the guess anyway.
Writes .bin (one byte per pixel) and .png when stem is given. A page-flipping game has more than one page; pass page_offset from the pages list to read the other.
Inputs: pid, stem, relocate, reference, +2 more. Returns: object
dosbox_read_memoryARead-only
Read bytes from the guest. With no segment this is the game's own data segment and offset may be a profile symbol name; with segment=0x1234 it is any segment in the guest -- another program in a launcher chain, a TSR, an overlay, or the interrupt vector table at segment 0. Choose a format: hex (raw bytes), u8/u16/u32 (little-endian numbers), or text (NUL-terminated string). This is how to read game state -- money, a unit's health, which screen is up -- without any of it being visible on screen.
Inputs: offset (required), pid, format, length, +1 more. Returns: object
dosbox_read_traceARead-only
Read what an installed trace has recorded: the values from its most recent hit, and how many times it has fired. A hit count of zero means the traced call has not run -- which is itself an answer.
Inputs: pid, handle. Returns: object
dosbox_recordA
Record the guest's sound for a stretch of time and return the file that appeared: an OPL register log (.dro), the MIDI stream (.mid) or the mixer output (.wav). This is the only independent record of what a game's sound driver actually writes. Arm it EARLY -- DOSBox-X starts an OPL capture at the guest's first register write, so arming late does not truncate the capture, it starts it somewhere else entirely.
Inputs: seconds (required), pid, kind, config, +2 more. Returns: object
dosbox_remove_traceBDestructive
Remove an installed trace: the call site is restored first so nothing new can enter the cave, then the cave is blanked.
Inputs: pid, handle. Returns: object
dosbox_sampleA
Sample a set of memory fields repeatedly and write the series as a TSV. dosbox_read_memory answers what a value is and dosbox_wait_for answers when it becomes something; this answers how it MOVES, which is what most questions about a running program actually are. Every field in a row comes from one snapshot, so the columns do not drift against each other. watch takes symbol names, offsets, or the name of a watch set the profile defines. The whole segment at the entry instant is dumped beside the TSV, so a question nobody thought to put in the watch list can still be answered offline.
Inputs: watch (required), hz, out, pid, +3 more. Returns: object
dosbox_search_memoryARead-only
Search the guest's 640 KiB of conventional memory for a byte pattern and report every hit as a guest segment:offset. This is how a known string -- a file name the game opens, a message it prints -- becomes the segment that holds it, which is the first step in writing a profile. Give text, or hex for raw bytes.
Inputs: hex, pid, text, limit. Returns: object
dosbox_send_keysA
Type into the guest by appending to its BIOS keyboard ring buffer -- no window focus, no host keystrokes. Each item is a single character or a key name (space, enter, esc, tab, backspace, up, down, left, right, home, end, pgup, pgdn, plus, minus, f1..f10). Keys are paced apart so the game consumes them one screen at a time; the ring holds 15, so send small batches and let the game catch up. Fails cleanly if the ring is full, which means the game is waiting on the mouse, not the keyboard.
Inputs: keys (required), pid, pace_seconds. Returns: object
dosbox_sessionsBRead-only
List attached sessions and any other visible DOSBox-X windows on the machine, with their process ids.
Inputs: window_title. Returns: object
dosbox_view_screenARead-only
Look at the emulator's screen. Returns the current frame as an image, photographed from the window without activating it. Use this to see where the game actually is between steps; use dosbox_capture_screen when the frame has to be exact and saved.
Inputs: pid, max_width.
dosbox_wait_forARead-only
Block until a memory field satisfies a test, or the timeout runs out. Use it to wait for a screen transition, an animation to finish, or a state byte to settle, instead of guessing at sleeps. Returns whether the test was satisfied and the last value seen -- it does not raise on timeout, so a caller can decide what a timeout means.
Inputs: offset (required), pid, size, any_of, +6 more. Returns: object
dosbox_watch_framesA
Watch the guest's video memory and return every distinct frame it put up, with the moment each appeared. This is how to measure an animation, a fade or a transition -- reading video memory does not slow the guest the way filming the window does, so the timings are the guest's own. Frames are written as _NNN.png.
Inputs: seconds (required), pid, stem, reference, +3 more. Returns: object
dosbox_write_memoryADestructive
Patch the game's data segment. This is how to set up a state that would otherwise take hours of play -- a treasury, a rank, a flag -- so a particular screen or code path can be reached. Writes land in the running game and are not undoable; the game may recompute a field shortly after it is written, so patch at a moment where it will be read before it is recomputed.
Inputs: writes (required), pid, segment. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 26 tools
The tools cluster into clear capability areas, but within some clusters the boundaries are narrow: view_screen, capture_screen, read_framebuffer, and watch_frames all provide screen-related output, and launch/attach/find_guest all establish a guest context in different ways. The detailed descriptions make the distinctions learnable, but an agent would need to read them carefully before selecting the right tool.
The dosbox_ prefix plus snake_case is used consistently, and most tools follow a clear verb_noun pattern such as send_keys, read_memory, and install_trace. A few resource/query names such as dosbox_profiles, dosbox_sessions, and dosbox_capabilities drop the verb form, which is a minor but noticeable inconsistency.
26 tools is large and heavier than the conventional well-scoped 3-15 range, but the tool set represents a genuinely broad and specialized domain: process management, input, memory inspection/editing, frame capture, tracing, and recording. The size feels deliberately broad rather than padded.
The set covers the full workflow of driving and inspecting a DOSBox-X guest: launch/attach/quit, keyboard/mouse input, read/write/search/dump memory, sample state over time, watch screen frames, and install/read/remove code traces. No major dead-end operations are apparent that would force the agent to leave the server to complete a task.
Maintenance
Related MCP Connectors
Securely control computers you explicitly pair through files, terminals, processes, screenshots, desktop UI/input, clipboard, browser automation, diagnostics, and document tools.
Eyes and hands on real Windows PCs — observe, click, type via Glasswarp API.
Drive real devices from your AI Coding tool. Embed a client SDK (Unity, Godot, Flutter, iOS/macOS, Android, React Native, Web) in your app, then capture screenshots, traverse the UI tree, inject taps and key events, and run automated test tasks on the physical device over a secure relay.
Remote shell and detached long-running jobs on your own machines — no SSH, open ports or VPN.
Related MCP Servers
- 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
- AlicenseBqualityBmaintenanceBridges 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.4724 PyPI3GPL 2.0
- FlicenseAqualityDmaintenanceEnables an MCP client to observe and control a text-mode DOS system via a Python bridge, supporting keyboard input and screen capture.8-
- FlicenseNot gradedqualityCmaintenanceThis MCP server enables AI agents to inspect and control the native DOSBox-X debugger through bounded MCP tools, including breakpoint management, execution control, register/memory inspection, and disassembly for DOS program debugging and reverse engineering.1-