Skip to main content
Glama

MSX-AI

Control emulated and/or physical MSX computers through one Python MCP server.

MSX-AI gives an MCP client a common set of tools for direct openMSX automation, an agent running on physical MSX hardware, or that same physical-agent path simulated through openMSX.

What you can do

  • Read screens, capture host-rendered PNG screenshots, and inspect CPU context.

  • Type BASIC, send BIOS-visible keys, load applications, automatically submit FE-header BLOAD binaries through resident MSX BASIC, and inject Z80 code.

  • Inspect or patch RAM and VRAM, and access I/O, slots, and mapper segments where the selected runtime can do so safely.

  • Transfer arbitrary MSX-DOS files with CRC-32, durable resume, atomic publication, optional PackBits compression, progress, and cancellation.

  • Use the same framed agent protocol with a simulated target and a real MSX.

  • Search a bundled, hash-verified documentation corpus directly through MCP.

Related MCP server: dos-mcp

Install and start

MSX-AI requires Python 3.10 or newer. Until the first PyPI release, install the current public source in an isolated environment:

git clone https://github.com/rgmgbr/msx-ai.git
cd msx-ai
pipx install .
msx-ai-mcp

After publication on PyPI, the shorter equivalent will be:

pipx install msx-ai
msx-ai-mcp

The default transport is STDIO. MCP configuration belongs to the client or to the developer machine; this repository intentionally does not ship a root .mcp.json. Such a file commonly grows workstation-specific executable paths and environment values, so it is ignored and excluded from distributions.

Register the server in an MCP client

Every client needs the same thing: run the STDIO command msx-ai-mcp and name the server msx-ai. Only the file format and the registration command differ.

Claude Code, from the CLI:

claude mcp add msx-ai -- msx-ai-mcp                 # this project only
claude mcp add --scope user msx-ai -- msx-ai-mcp    # every project

claude mcp list then reports the server as connected, and the tools appear as mcp__msx-ai__msx_local_* and mcp__msx-ai__msx_agent_*. A session that was already running does not pick the server up: the server list is resolved when the session starts, and the /mcp reconnect only re-dials entries that are already in that list. Start a new session after adding the server.

Codex, from the CLI:

codex mcp add msx-ai -- msx-ai-mcp

or as a portable entry in the user-level ~/.codex/config.toml or, for a trusted checkout only, the ignored project file .codex/config.toml:

[mcp_servers.msx-ai]
command = "msx-ai-mcp"

The ChatGPT desktop app, Codex CLI, and Codex IDE extension share that Codex configuration, as described in the official Codex MCP setup.

Clients that read a JSON configuration — Claude Desktop, the VS Code MCP integration, Cursor, Windsurf, Zed, and others — express the same server with their own local file:

{
  "mcpServers": {
    "msx-ai": {
      "command": "msx-ai-mcp"
    }
  }
}

If msx-ai-mcp is not on the PATH seen by the client, use the absolute path of the entry point produced by the installation, for example ~/.local/bin/msx-ai-mcp or <checkout>/.venv/bin/msx-ai-mcp, and keep that path in the local client file only.

Keep any OPENMSX_BIN override in the shell environment or in that local client configuration. Do not commit an absolute path from one workstation.

Local Streamable HTTP is optional:

msx-ai-mcp --transport http --host 127.0.0.1 --port 8000

The HTTP endpoint is http://127.0.0.1:8000/mcp. It is intentionally restricted to unauthenticated IPv4 loopback. STDIO remains the recommended default.

Choose an explicit channel

MSX-AI has no mutable “active backend.” msx_local_* tools always use the openMSX control API, and msx_agent_* tools always use the ASM-agent protocol. Both channels may remain connected, and calls may alternate between them in any order without changing the route of a later call. msx_targets_status lists the independent channel identities but never selects one.

For configurations made from an earlier checkout, migrate deliberately:

Earlier name

Explicit replacement

msx_status

msx_local_status, msx_agent_status, or inventory-only msx_targets_status

msx_shutdown

msx_local_shutdown, msx_agent_disconnect, or msx_tcp_bench_shutdown

msx_real_listen

msx_agent_listen

Other msx_<operation> names

Choose msx_local_<operation> or msx_agent_<operation> for the intended channel

Ambiguous names are intentionally not published and never route according to the most recently connected target.

openMSX discovery and preflight

msx_local_doctor is read-only and does not start an emulator. Run it before the first boot, especially after installing openMSX or changing profiles. It reports the host platform, resolved executable/presence, owned and attach transports/support (control_transport_supported, boot_supported, and attach_supported), configuration mode/homes, requested and resolved profile/machine, readiness, and structured issues. Candidate reports repeat the transport and boot support flags. Every issue includes an action field with the concrete recommendation.

Executable discovery is host-aware:

Host

Discovery order and owned-process control

Linux

OPENMSX_BIN, then openmsx on PATH; control_transport=stdio, attach_transport=unix_socket

macOS

OPENMSX_BIN, PATH, then the standard openMSX app bundle; control_transport=stdio, attach_transport=unix_socket

Windows

OPENMSX_BIN, PATH, then registered/standard Program Files installs; control_transport=tcp_sspi, attach_transport=tcp_sspi

The portable cbios profile uses the free C-BIOS supplied with openMSX and does not require proprietary firmware. It is intended for control-channel, screen, and cartridge-oriented smoke tests; C-BIOS does not include MSX BASIC or MSX-DOS. auto first resolves a usable configured BASIC machine and otherwise falls back to C-BIOS. The existing basic, disk, dos, and msx2plus profiles remain available for installations with their required firmware and media. Omitting profile still means basic for compatibility; request auto or cbios explicitly for a portable first boot.

Boot configuration is explicit:

config_mode

Behavior

isolated

Default; uses only MSX-AI's managed openMSX home and temporary settings

user

Uses the user's openMSX machine/file pools without replacing the user's persistent settings

overlay

Adds MSX-AI's managed templates while retaining user machine/ROM discovery

Direct openMSX

Use this path for fast emulator automation and exact debugger snapshots. It does not require TCP or the MSX-side agent.

  1. Call msx_local_doctor with profile="auto" and the intended config_mode; resolve any reported issue.

  2. Call msx_local_boot with profile="auto", optionally window=true, and the same config_mode.

  3. Call msx_local_status; confirm backend is openmsx.

  4. Call msx_local_screen. Use msx_local_run_basic only when the doctor resolved a firmware-backed profile that provides MSX BASIC.

Use msx_local_attach instead when openMSX is already running. MSX-AI then shares that instance without changing its power, throttle, or audio state. If several live openMSX sockets exist, the call refuses to guess: repeat it with one of the exact socket_path values reported by the error. Linux and macOS attach through the published Unix-domain socket. On Windows, both owned boot and attach use openMSX's loopback descriptor (ports 9938 through 10001) and the official SSPI Negotiate exchange. An owned boot waits for the descriptor matching the child PID; attach additionally validates the selected existing process. If SSPI is unavailable, MSX-AI reports Windows control as unsupported with an actionable recommendation rather than opening an unauthenticated raw TCP channel.

Simulated physical agent

Use this path to validate the real resident/monitor protocol and restrictions without physical hardware.

This workflow is source-checkout-only because it builds the agent and isolated bench locally. A pipx-installed host can use it when MSX_AI_SOURCE_ROOT points to a checkout.

  1. In that checkout, build the agent suite with make agent and prepare the local test ROMs and MSX-DOS/Nextor image.

  2. Call msx_tcp_bench_start with mode="resident"; add window=true for a visible emulator.

  3. Call msx_tcp_bench_status and verify both explicitly identified channels.

  4. Use msx_agent_* to validate the physical protocol path and msx_local_* to inspect the same emulated machine through openMSX control APIs.

Use mode="monitor" for direct call, run, stop, slot, and mapper experiments. The bench uses exactly one isolated openMSX process. Its local and agent channels carry the same bench_id, so callers can alternate between them without spawning, attaching, or selecting another emulator. A stalled agent does not prevent msx_local_screenshot from diagnosing the existing machine.

Physical MSX

Use this path when actual MSX hardware behavior is the subject of the session.

  1. Install the twelve-file suite described below.

  2. For a UART bridge, run MSXAI /DRIVER:8251 or MSXAI /DRIVER:16C550. Configure 8251 for 19200 baud/8N1; configure 16C550 for 57600 baud/8N1 with hardware RTS/CTS. If the bridge connects outward, call msx_agent_listen on the host's specific LAN IPv4 address; if it accepts connections, call msx_agent_connect with its IPv4 address. The 16C550 default is exactly 57600; /57600 makes it explicit and /115200 selects the faster UART divisor for both resident and monitor modes. Do not use either baud option with 8251 or UNAPI. For a BaDCaT/ZiModem server, disconnect the host first, run MSXAI /UNINSTALL, then BADINIT followed by the matching MSXAI /DRIVER:16C550 command. BADINIT defaults to port 6603 and accepts /PORT:<1..65535> plus at most one of /57600 or /115200; the port and baud options may appear in either order, while duplicate options are rejected. The selected port applies only to the current modem session and must also be passed to msx_agent_connect. Unlike the UNAPI command below, ZiModem BADINIT accepts port 65535 because it is an ordinary TCP listener port there, not UNAPI's random-port sentinel. BADINIT never saves, reloads, resets, factory-resets, or changes the persistent-listener register. A power cycle therefore restores the user's previously saved modem state. Its bootstrap recovers from blocked XON/XOFF flow, observes ZiModem's delayed baud change, and reports failed UART probes as hexadecimal diagnostics.

  3. For an MSX Pico+ or an original MSX Pico equipped with Wi-Fi, first use the cartridge's existing Wi-Fi Setup. Then run MSXAI /DRIVER:UNAPI, or add /PORT:<1..65534> to choose a listener port other than the default 6603. UNAPI reserves 65535 (FFFFh) as a random-port sentinel, so it cannot be selected as a predictable listener endpoint. Call msx_agent_connect with the MSX's IPv4 address and the same port. After a resident connection is lost, the listener reopens silently. The host can reconnect to the same IP address and port without a command or key press on the MSX. This behavior has been validated on a physical HOTBIT 1.0 with an MSX Pico+ cartridge. /MONITOR remains available for foreground diagnostics.

  4. Before installing the resident, an optional make unapi-probe builds work/agent/UNAPIPRB.COM. Run UNAPIPRB or UNAPIPRB 43123 to verify discovery, passive capability, the effective IP/port, and TCP state; quit the probe before starting MSXAI on that port.

  5. The opt-in openMSX UNAPI validation exercises the same EXTBIO, passive-listener, bidirectional-stream, and relisten contract with a matched openMSXnet/UNAPINET pair. It is a contract emulator, not Pico/Pico+ firmware or bus-timing emulation.

  6. Call msx_agent_status before any mutation and verify runtime, transport, and feature negotiation.

The MSXAI agent core does not configure Wi-Fi, issue modem AT commands, or depend on a specific network-adapter brand. BADINIT.COM is a separate, optional BaDCaT/ZiModem session initializer; the generic 16C550 driver remains product-neutral. The UNAPI path discovers a TCP/IP UNAPI implementation by capability, so it is not tied to the Pico+ product name.

Reproducible demonstrations

With a visible direct-openMSX BASIC session, these MCP calls draw a simple test card and return a PNG without proprietary game media:

msx_local_boot(profile="basic", window=true)
msx_local_run_basic(program="10 SCREEN 2\n20 COLOR 15,4,4\n30 CIRCLE(128,96),50,15\n40 LINE(40,40)-(216,152),8,B\n50 GOTO 50")
msx_local_screenshot()

For an already installed physical agent, begin read-only and verify the selected path before doing anything mutable:

msx_agent_listen(host="192.168.1.20", port=6603)
msx_agent_status()
msx_agent_cpu_snapshot()

The status result is structured; a resident agent reports, for example, {"target":"agent","backend":"agent","state":"running","runtime_mode":"resident",...} before the snapshot call. An idle foreground monitor cannot provide a CPU snapshot; run a payload there first. The capability matrix below defines the two capture semantics.

Architecture

MCP client
    |
    v
MSX-AI Python server
    |
    +-- openMSX control API ----------------------> emulated MSX
    |
    `-- protocol v3 over a byte stream
            |
            `-- TCP/IPv4
                  |
                  +-- RS232-Net -----------------> simulated MSX agent
                  |
                  +-- transparent UART bridge --> physical MSX agent
                  |                                  |
                  |                                  +-- 8251
                  |                                  `-- 16C550-compatible UART
                  |
                  `-- TCP/IP UNAPI listener ------> physical MSX agent
                                                     `-- Pico/Pico+ Wi-Fi

The MCP interface, target protocol, network link, and MSX UART driver are separate layers. A user can run only direct openMSX, only a physical target, or both channels simultaneously without installing every optional backend. Tool names fix the route; connection order never changes it.

Capability matrix

Capability

Direct openMSX

Agent resident

Agent monitor

Text screen and standard PNG capture

Yes

Cooperative

Cooperative

CPU snapshot

Exact instruction boundary

H.TIMI callback context

H.TIMI callback while a payload runs; unavailable when idle

BIOS text and special-key input

Yes

Yes

No resident keyboard spool

RAM/VRAM inspect and patch

Yes

Bounded; page restrictions

Yes; monitor image protected

MSX-DOS PUT/GET

No

Yes

No

Application load

Yes

FE-header BLOAD through verified BASIC; safe direct data segments

Direct load/call/run

Direct call/run/stop

Yes

No

Yes

I/O-port access

Via expert Tcl/debug tools

Yes

Yes

Slot/mapper selection

Via expert Tcl/debug tools

No

Yes

Reset/reboot and raw openMSX Tcl

Yes

No

No

“Cooperative” is important: the resident runs through the normal BIOS timer hook. It is not an NMI, bus master, or universal freeze mechanism.

Install the MSX-side suite

The physical and simulated agent paths use these files:

A:\MSXAI\
  MSXAI.COM     MSXAIXF.COM  BADINIT.COM  MCP8251.TSR
  MCP16550.TSR  MCP115K.TSR  MCPUNAPI.TSR TU.COM
  MP.COM        MEMMAN.COM   TL.COM       TK.COM

Build them from source with make agent, or use matching binaries from a project release. Keep the suite together and configure MSX-DOS once:

SET MSXAI_HOME=A:\MSXAI
PATH A:\MSXAI;%PATH%

The default MemMan resident returns to MSX-DOS. MSXAIXF.COM is its transient foreground file helper. On a first UNAPI installation only, TU.COM runs after MemMan's warm boot and before TL.COM to prepare compatible Pico/Pico+ firmware state safely. UART installations invoke TL.COM directly and never use TU.COM; other UNAPI implementations keep their normal installation path. MP.COM remains the one-shot helper that runs after TL.COM and applies the selected UNAPI port, including the default 6603 when /PORT is omitted. The public /PORT syntax remains decimal; the compact hexadecimal form passed to MP.COM is private to the install chain. Internally, MP.COM and every existing-resident reconfiguration use the private MemMan A7h v3 lifecycle request: 20 bytes containing the port, a caller-owned 1 KiB page-2 stack, structured results, and the requested transport at offset 14 (0=8251, 1=16C550, 2=UNAPI). Offset 15 is the 16C550 divisor (2=57600, 1=115200) and must be zero for other targets. Offsets 16 through 19 return the local IPv4 address obtained from UNAPI. MP.COM always sets target 2 and a zero divisor because it is the first-install UNAPI port helper. The stack has 16-byte low/high guards and is placed below the TPA limit with 256 bytes of headroom from the caller's current SP. Thus driver changes and potentially deep TCP lifecycle work do not use MemMan's internal stack. The old A6h raw-port ABI is rejected, so mixed old/new suite files fail closed. The other COM/TSR files are part of the same versioned suite. MSX-DOS 2 or Nextor and a memory mapper are required for resident mode. After a successful UNAPI startup, resident and foreground modes both print MCP listening at: <IPv4>:<port>.

After that first installation, BASIC-to-DOS is a separate resident lifecycle. When _SYSTEM or CALL SYSTEM returns to DOS, the resident and its configured UNAPI listener are restored automatically. No second invocation of MSXAI is required.

MCP interface

The msx-ai-mcp entry point uses the official Python MCP SDK and negotiates current and supported older MCP protocol revisions. It provides:

  • STDIO and local Streamable HTTP transports.

  • Structured results and output schemas for every tool.

  • Explicit read-only, destructive, idempotent, and open-world annotations.

  • Cooperative MCP progress and safe cancellation for PUT/GET transfers.

  • Documentation resources under msx-ai://docs/.

  • The read-only msx_docs_search tool.

  • Explicit msx_local_*, msx_agent_*, and paired msx_tcp_bench_* families; ambiguous generic operational names are not published.

  • start_msx_session and diagnose_msx_connection prompts.

Start with msx-ai://docs/index when a client supports resources, or call:

msx_docs_search(query="resident screenshot safety")

Safety and current limits

  • Software that disables interrupts, replaces the timer-hook chain, or pages required state away can make a resident temporarily unreachable.

  • A resident CPU snapshot is the register frame visible at the BIOS callback boundary. It must not be described as the interrupted application's exact arbitrary PC/SP. Direct openMSX snapshots have exact debugger semantics.

  • Resident RAM page 1 is reserved while servicing requests. Page 3 contains live BIOS, DOS, stack, hook, and system state; arbitrary writes can crash the machine.

  • Automatic BLOAD loading accepts only a complete segment in 0x8000-0xFFFF, verifies that declared payload, and never relocates it. An incompatible range or entry is rejected; verification proves the RAM bytes, not that arbitrary machine code will cooperate with the agent.

  • A paired bench exposes two control channels to one machine, not two machines. Local reset, power, quit, and local shutdown are refused while the paired bench exists, even after agent disconnect; use msx_tcp_bench_shutdown for the combined lifecycle.

  • Resident input feeds the BIOS keyboard ring. Games that scan the physical keyboard matrix directly will not observe it.

  • Screenshots reconstruct standard SCREEN 0-8 and 10-12 from readable state. SCREEN 9, raster effects, exact interlaced timing, and write-only palette changes have limits. Large captures are expensive over slow 8251 links.

  • PUT/GET never publishes an incomplete file as success. Cancellation keeps verified recovery state so the operation can resume.

  • Atomic, no-overwrite GET publication and first-run openMSX-template materialization require hard-link support on the relevant host filesystem. FAT/exFAT destinations are therefore unsuitable. If publication is refused, the verified .msxpart file and journal remain available for resume on a supported destination.

  • TCP agent framing is binary but has no authentication, encryption, TLS, or replay protection. CRC detects accidental corruption; it is not a security mechanism. Because the channel exposes RAM, VRAM, I/O, and execution, keep it on an isolated trusted LAN or carry it through a VPN/secure tunnel. Never expose it directly to the internet.

  • MSX_AI_USER_ROOT is the base for relative host paths, not a filesystem sandbox. Absolute paths remain available to explicit MCP calls with the same permissions as the server process; use an appropriately restricted account and working directory.

  • ROMs and bootable disk images are not distributed. Automatic resident UNAPI listener recovery has been validated on a physical HOTBIT 1.0 with an MSX Pico+ cartridge. Other physical configurations, measured performance, and BaDCaT SMD remain separate hardware-validation targets.

Development and validation

The hardware-free host is continuously tested on Ubuntu, Windows, and macOS. Ubuntu covers Python 3.10 through 3.14; Windows and macOS run the current Python 3.14 lane. Every host lane installs the project, runs the complete unit suite, and exercises the installed MCP entry point through STDIO and IPv4 loopback HTTP. The Ubuntu-only release gate additionally rebuilds the Z80 suite with the pinned assembler. These regular jobs validate the host and simulated TCP protocol path without launching an emulator. The ROM-free C-BIOS smoke can be run explicitly on Ubuntu, Windows, or macOS; the committed CI does not currently schedule a real-emulator job. That smoke performs the same read-only adapter preflight used by the doctor, boots through both adapter and public Session/profile paths, exercises the platform control transport, reads emulator state, and shuts down without an orphan process. The larger MSX-DOS/RS232-Net suite with local media and physical Pico/Pico+ or BaDCaT hardware remain separate explicit integration gates.

TCP/IP UNAPI has a second opt-in emulator gate. Set the paths documented in docs/openmsx-unapi-validation.md, run make unapi-emulation-preflight, then make test-unapi-emulation. The normal unit and release suites never download that emulator or install host libraries.

For an editable environment:

python3 -m venv .venv
. .venv/bin/activate
python -m pip install -e . 'build>=1' 'setuptools>=77'
make PYTHON=python test
make PYTHON=python agent
make PYTHON=python release-check

The equivalent PowerShell environment setup is:

python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e . "build>=1" "setuptools>=77"

Those Make commands remain the supported Linux/macOS workflow. On Windows, after activating the environment, the equivalent release gate does not require installing a Unix Make implementation:

python tools/release_check.py

Windows uses the portable Python agent builder by default. An explicit MAKE override opts into the existing Makefile recipe and is validated, while Linux/macOS continue to require and use Make exactly as before.

The unit and explicit integration suites together cover protocol framing, backend isolation, memory safety, keyboard input, screenshots, CPU snapshots, application loading, transfer recovery, MCP metadata/resources, packaging paths, and openMSX integration. The release gate additionally requires Bas Wijnen z80asm 1.8 on PATH (or Z80ASM set to that executable). It builds and installs the wheel outside the source tree and exercises both STDIO and local HTTP with an official MCP client. This regular gate can validate uncommitted work. Once the intended tree is committed and clean, run the strict committed-source gate or persist the release assets with:

make PYTHON=python publish-check
make PYTHON=python release-assets

The corresponding direct Windows commands are python tools/release_check.py --publish and python tools/release_check.py --publish --output-dir dist.

release-assets publishes the versioned Python source distribution and wheel, plus the stable, MSX-DOS-compatible MSXAI.ZIP and its standalone README.TXT, under dist/. The ZIP contains the same README, the twelve matching MSX files, the project license, the MemMan notice, checksums, and explicit wire-protocol, transfer-protocol, and toolchain metadata. Existing artifacts are never overwritten. The gate proves same-host equivalence with the pinned assembler; it does not claim byte-identical output across unrelated toolchains.

The full agent-through-emulator end-to-end suite is deliberately separate and serialized. With the required ROMs and ignored MSX-DOS/Nextor image installed, opt in with:

make PYTHON=python test-integration

Releases follow Semantic Versioning from the single value in server/_version.py and require a matching annotated vMAJOR.MINOR.PATCH tag. That same value is generated into the MSXAI.COM banner/help and the released README.TXT, so the installed MSX-side build can be identified on the machine. Runtime compatibility is still determined by the negotiated wire protocol, capabilities, and versioned subprotocols.

Documentation

The bundled corpus is original project documentation. Its manifest records GPL-3.0-or-later licensing, internal evidence paths, review date, and a SHA-256 digest for every MCP-readable document. Third-party MemMan material is identified separately in third_party/memman/NOTICE. The small openMSX configuration resource set includes adapted GPL-2.0 material and is identified in third_party/openmsx/NOTICE; it does not change the license of the independent MSX-AI host, agent, or documentation.

License

MSX-AI code, project-authored documentation, and original artwork are released under GPL-3.0-or-later. Bundled third-party or derived resources retain the licenses named in their notices. Copyright (C) 2026 Rodrigo Galhardi M. Garcia. Project authorship and the origin of the real-MSX MCP integration are recorded in AUTHORS.md. The original mascot artwork and its provenance statement are described in assets/NOTICE.md.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    MCP server for RetroArch via its Network Control Interface. Drive any libretro core — read/write memory, save/load state, screenshot, pause/frame-advance/reset — across NES, SNES, Genesis, N64, GBA, PS1 and more.
    17
    14 npm
    6
    MIT
  • F
    license
    A
    quality
    D
    maintenance
    Enables an MCP client to observe and control a text-mode DOS system via a Python bridge, supporting keyboard input and screen capture.
    8
    -
  • A
    license
    B
    quality
    B
    maintenance
    MCP server providing emulator control for a loopback-only DOSBox-X QMP server, with tools for keyboard input, memory reads, screenshots, and state snapshots to complement Ghidra's GDB TraceRMI debugging.
    13
    2
    Apache 2.0
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables controlling and interacting with a Commodore 64 emulator running in the browser via MCP.
    -