Skip to main content
Glama
BizHawk Emulator MCP

BizHawk Emulator MCP

by KanonZombie

BizHawk Emulator MCP

Deterministic BizHawk control over MCP for Game Boy, Mega Drive/Genesis, NES, and SNES.

CI License GitHub stars

What is this?

This project exposes BizHawk as an MCP server. It sends input, frame-step, screenshot, reset, savestate, and memory-read commands through a portable Lua bridge that exchanges atomic files with Python. JSON scenarios make emulator checks repeatable without requiring a BizHawk plugin or a socket service.

The package does not include BizHawk or ROMs. Use legally obtained ROMs and install BizHawk separately.

Related MCP server: mcp-mgba

Quick start

The supported deployment is a normal Python installation plus one project home shared by the MCP client and BizHawk.

git clone https://github.com/KanonZombie/emulator-mcp.git
Set-Location emulator-mcp

python -m venv .venv
& .\.venv\Scripts\python.exe -m pip install .
& .\.venv\Scripts\emulator-mcp.exe init .emulator-mcp
& .\.venv\Scripts\emulator-mcp.exe --version

init creates the portable bridge and empty runtime directories:

.emulator-mcp/
  bizhawk/bridge.lua
  runtime/gb/
  runtime/md/
  runtime/nes/
  runtime/snes/

The command is safe to run again after upgrading the package; it refreshes the bridge and keeps existing runtime files.

Start BizHawk

  1. Open a legally obtained ROM in BizHawk.

  2. Open Tools > Lua Console.

  3. Load .emulator-mcp/bizhawk/bridge.lua.

  4. Leave the Lua script running. BizHawk stays paused between commands.

The bridge detects the loaded core and selects its runtime automatically:

BizHawk core

MCP target

Buttons

Game Boy / Game Boy Color

gb

A, B, Start, Select, D-pad

Genesis / Mega Drive

md

A, B, C, X, Y, Z, Mode, Start, D-pad

NES

nes

A, B, Start, Select, D-pad

SNES

snes

A, B, X, Y, L, R, Start, Select, D-pad

Run one BizHawk instance per active target. Each instance must load the bridge from the same .emulator-mcp directory configured for the MCP client.

Run directly from a checkout

For local development, you can use the repository as the project home without running init. In PowerShell, set the environment variable before launching BizHawk. Replace these example paths with the locations on your machine:

$env:EMULATOR_MCP_HOME = "C:\path\to\emulator-mcp"

& "C:\path\to\BizHawk\EmuHawk.exe" `
  "--lua=C:\path\to\emulator-mcp\bizhawk\bridge.lua" `
  "C:\path\to\roms\game.gb"

This starts BizHawk with the Game Boy ROM and the repository bridge. Runtime files are written under runtime/gb/, which is ignored by Git. The variable only applies to the current PowerShell session; configure the same EMULATOR_MCP_HOME in the MCP client if it is launched separately.

Configure an MCP client

Use absolute paths. Replace both placeholders in this example with the paths on the machine that runs the client:

{
  "mcpServers": {
    "bizhawk": {
      "command": "C:\\path\\to\\emulator-mcp\\.venv\\Scripts\\emulator-mcp.exe",
      "env": {
        "EMULATOR_MCP_HOME": "C:\\path\\to\\emulator-mcp\\.emulator-mcp"
      }
    }
  }
}

EMULATOR_MCP_HOME is the authoritative project home. The Python server and the Lua bridge must point to the same directory. If the command is installed with pipx, use emulator-mcp as the command and keep the same environment variable.

Verify the connection

These commands use the same implementation as the MCP tools and require the bridge to be running in BizHawk:

& .\.venv\Scripts\emulator-mcp.exe --target gb --test bridge-info
& .\.venv\Scripts\emulator-mcp.exe --target gb --test status
& .\.venv\Scripts\emulator-mcp.exe --target nes --test buttons
& .\.venv\Scripts\emulator-mcp.exe --target snes --test tap START 8 2
& .\.venv\Scripts\emulator-mcp.exe --target snes --test screenshot title

bridge-info should report bridge version 1.1.0. A timeout normally means that the Lua script is not running or EMULATOR_MCP_HOME points to a different directory.

CLI command reference

With no arguments, the installed executable starts the stdio MCP server. With --test, it runs one diagnostic command and prints its result.

Process commands

Command

Arguments

Description

emulator-mcp

none

Start the MCP server over stdio.

emulator-mcp --help

none

Show short help; -h is also accepted.

emulator-mcp --version

none

Print the package version.

emulator-mcp init

[directory]

Copy bridge.lua and create runtime directories. Defaults to .emulator-mcp.

emulator-mcp --target TARGET --test COMMAND

see below

Run a diagnostic against gb, md, nes, or snes; target defaults to gb.

init is idempotent: it refreshes the copied bridge without removing runtime files. Use this prefix for the diagnostic commands:

& .\.venv\Scripts\emulator-mcp.exe --target gb --test COMMAND [ARGUMENTS]

Emulator test commands

Command

Arguments and defaults

Description

status

none

Return system, frame, and screen size.

bridge-info

none

Return bridge version, detected system, and capabilities.

buttons

none

Return BizHawk button names and current states.

read-memory

<address> [length=1] [domain=System Bus]

Read a memory range and return its bytes as uppercase hexadecimal. Addresses accept decimal or 0x hexadecimal notation; length is limited to 64 KiB.

step

[frames=1]

Advance neutral input; frames are clamped to 1--600.

tap

<button> [hold_frames=8] [release_frames=2]

Tap a button; hold is clamped to 1--120 and release to 1--600.

hold

<button> [frames=1]

Hold a button; frames are clamped to 1--120.

release

[frames=2]

Release all buttons; frames are clamped to 1--600.

reset

[settle_frames=60]

Reboot and settle with neutral input; frames are clamped to 1--600.

press

<button> [frames=1]

Legacy alias for tap with two release frames.

save-state

[name=state]

Save a state under the selected target runtime.

load-state

[name=state]

Load a state from the selected target runtime.

create-baseline

[name=baseline]

Alias for save-state.

load-baseline

[name=baseline]

Alias for load-state.

screenshot

[name=shot]

Save a screenshot under the selected target runtime.

gb-gpu-snapshot

[name=snapshot] [--no-render]

Capture Game Boy DMG GPU data; target must be gb.

run-scenario

<path>

Run a JSON scenario against the selected target.

run-pair-scenario

<path>

Run the GB and Mega Drive entries in a pair scenario.

build-contact-sheet

<pair_summary_path>

Rebuild a pair contact sheet from an existing summary.

build-pair-diffs

<pair_summary_path>

Rebuild crops, diffs, and the pair contact sheet.

gb-gpu-snapshot --no-render skips diagnostic PNG panels. Pair commands use the pair file's GB/MD entries; the target option is not needed.

& .\.venv\Scripts\emulator-mcp.exe --target md --test status
& .\.venv\Scripts\emulator-mcp.exe --target gb --test tap A 8 2
& .\.venv\Scripts\emulator-mcp.exe --target md --test read-memory 0xFFE75E 2 "M68K BUS"
& .\.venv\Scripts\emulator-mcp.exe --target gb --test read-memory 0xC000 16 "System Bus"
& .\.venv\Scripts\emulator-mcp.exe --target gb --test save-state title_screen
& .\.venv\Scripts\emulator-mcp.exe --test run-pair-scenario scenarios/pairs/title_start.json

Memory read examples

read-memory uses the exact BizHawk memory-domain name. These examples read the main RAM and video memory for each supported console:

Mega Drive / Genesis

# Main 68K RAM: bus address FF0000--FFFFFF
& .\.venv\Scripts\emulator-mcp.exe --target md --test read-memory 0xFFE75E 16 "M68K BUS"

# VDP video RAM: domain-relative address 0000--FFFF
& .\.venv\Scripts\emulator-mcp.exe --target md --test read-memory 0x0000 16 "VRAM"

Game Boy / Game Boy Color

# Work RAM (WRAM): CPU address C000--DFFF
& .\.venv\Scripts\emulator-mcp.exe --target gb --test read-memory 0xC000 16 "System Bus"

# Video RAM (VRAM): CPU address 8000--9FFF
& .\.venv\Scripts\emulator-mcp.exe --target gb --test read-memory 0x8000 16 "System Bus"

For Mega Drive, 0xE75E in a RAM Watch RAM domain corresponds to 0xFFE75E in M68K BUS. Sign-extended values such as 0xFFFFE75E are normalized to the same bus address. Memory reads return uppercase hexadecimal bytes and do not advance the emulator frame.

MCP tools

Generic tools accept target="gb", "md", "nes", or "snes":

Tool

Parameters

Description

emulator_status

target="gb"

Return system, frame, and screen size.

emulator_bridge_info

target="gb"

Return bridge version and capabilities.

emulator_buttons

target="gb"

Return BizHawk button names and states.

emulator_read_memory

target="gb", address, length=1, domain="System Bus"

Read a memory range and return uppercase hexadecimal bytes. Maximum length is 64 KiB.

emulator_step

target="gb", frames=1

Advance neutral input; frames 1--600.

emulator_tap

target="gb", button="", hold_frames=8, release_frames=2

Tap one button; hold 1--120 and release 1--600.

emulator_hold

target="gb", button="", frames=1

Hold one button; frames 1--120.

emulator_release

target="gb", frames=2

Release all buttons; frames 1--600.

emulator_reset

target="gb", settle_frames=60

Reboot and settle; frames 1--600.

emulator_screenshot

target="gb", name="shot"

Save a screenshot and return its path.

emulator_save_state

target="gb", name="state"

Save a named state.

emulator_load_state

target="gb", name="state"

Load a named state.

emulator_create_baseline

target="gb", name="baseline"

Alias for saving a named state.

emulator_load_baseline

target="gb", name="baseline"

Alias for loading a named state.

emulator_run_scenario

path, target="gb"

Run a JSON scenario against a target.

Additional tools cover Game Boy GPU snapshots and GB/Mega Drive pair scenarios:

Tool

Parameters

Description

emulator_gb_gpu_snapshot

target="gb", render=True, include_raw=True, output_dir=None, name="snapshot"

Capture DMG GPU data and optionally render PNG panels. Only target gb.

gb_gpu_snapshot

render=True, include_raw=True, output_dir=None

Legacy short alias for the Game Boy snapshot tool.

emulator_run_pair_scenario

path

Run the GB and Mega Drive scenarios in a pair definition.

emulator_build_pair_contact_sheet

pair_summary_path

Rebuild a pair contact sheet.

emulator_build_pair_diffs

pair_summary_path

Rebuild crops, visual diffs, and the contact sheet.

The original Game Boy-only MCP names remain available as legacy aliases:

Tool

Parameters

Description

bizhawk_status

none

Alias for emulator_status(target="gb").

bizhawk_bridge_info

none

Alias for emulator_bridge_info(target="gb").

bizhawk_step

frames=1

Alias for emulator_step(target="gb").

bizhawk_tap

button, hold_frames=8, release_frames=2

Alias for emulator_tap(target="gb").

bizhawk_hold

button, frames

Hold a Game Boy button; frames 1--120.

bizhawk_release

frames=2

Alias for emulator_release(target="gb").

bizhawk_reset

settle_frames=60

Alias for emulator_reset(target="gb").

bizhawk_press

button, frames=1

Legacy tap alias with two release frames.

bizhawk_screenshot

name="shot"

Alias for emulator_screenshot(target="gb").

bizhawk_save_state

name

Save a named Game Boy state.

bizhawk_load_state

name

Load a named Game Boy state.

bizhawk_create_baseline

name

Save a named Game Boy baseline.

bizhawk_load_baseline

name

Load a named Game Boy baseline.

bizhawk_read_memory

address, length=1, domain="System Bus"

Legacy Game Boy memory-read alias.

bizhawk_run_sequence

operations

Run a list of scenario operations against Game Boy.

bizhawk_run_scenario

path

Run a JSON scenario against Game Boy.

Scenario operations

The steps array accepts one operation object per sequence:

Operation

Fields

Description

tap

tap, hold_frames=8, release_frames=2

Tap a button.

press

press, frames=8, release_frames=2

Legacy tap spelling.

hold

hold, frames=1

Hold a button.

release

release

Release all buttons for that many frames.

step

step

Advance neutral frames.

screenshot

screenshot

Capture a named screenshot in the run directory.

gb_gpu_snapshot

gb_gpu_snapshot

Capture a named Game Boy GPU snapshot.

gpu_snapshot

string or {name, render, include_raw}

Capture a GPU snapshot with options.

Scenarios can use start.mode values none, reset, or load_state, plus the legacy top-level load_state and optional save_state_after fields.

Input semantics

  • tap holds one button and adds neutral frames before/after it.

  • hold holds one button without an automatic final release.

  • release releases every known button for the requested number of frames.

  • step advances with neutral input.

  • reset reboots the core and advances neutral settling frames.

Frame counts are bounded by both the server and the bridge. Button names are case-insensitive. Memory reads return an uppercase hex field and do not advance the emulator frame. For Mega Drive, use the exact M68K BUS domain; the sign-extended address 0xFFFFE75E is normalized to bus address 0xFFE75E, corresponding to offset 0xE75E in the main RAM domain.

Scenarios

Scenarios are plain JSON. Paths are resolved relative to the scenario file for pair scenarios and relative to the current directory for direct CLI use.

{
  "id": "nes.title_start",
  "description": "Boot and press Start",
  "start": { "mode": "reset", "settle_frames": 60 },
  "steps": [
    { "step": 300 },
    { "screenshot": "title" },
    { "tap": "START", "hold_frames": 8, "release_frames": 2 },
    { "step": 60 },
    { "screenshot": "after_start" }
  ],
  "save_state_after": "after_start"
}

Run a scenario with:

& .\.venv\Scripts\emulator-mcp.exe --target nes --test run-scenario scenarios/nes/title_start.json

Supported starts are none, reset, and load_state. Run artifacts are written below .emulator-mcp/runtime/<target>/runs/ and are intentionally ignored by Git.

Configuration

Variable

Purpose

EMULATOR_MCP_HOME

Absolute project home shared by Python and BizHawk.

BIZHAWK_GB_BRIDGE_DIR

Optional Game Boy runtime directory override.

BIZHAWK_MD_BRIDGE_DIR

Optional Mega Drive runtime directory override.

BIZHAWK_NES_BRIDGE_DIR

Optional NES runtime directory override.

BIZHAWK_SNES_BRIDGE_DIR

Optional SNES runtime directory override.

BIZHAWK_BRIDGE_DIR

Legacy Game Boy runtime override.

Prefer EMULATOR_MCP_HOME; the target-specific variables exist for compatibility and isolated runtime layouts. Empty overrides are ignored.

Project structure

.github/
  workflows/
bizhawk/
docs/
  ai/
scenarios/
  gb/
  md/
  nes/
  pairs/
  snes/
server/
  tests/
AGENTS.md
LICENSE
README.md
pyproject.toml
requirements.txt

Generated directories are deliberately absent from the tree: .venv/, build/, dist/, runtime/, and .emulator-mcp/ stay local.

Development and release checks

python -m venv .venv
& .\.venv\Scripts\python.exe -m pip install .
& .\.venv\Scripts\python.exe -m unittest discover -s server/tests -v
& .\.venv\Scripts\python.exe -m pip wheel . --no-deps -w dist

The unit tests do not require an emulator. For a BizHawk release smoke test, provide the executable and local ROMs explicitly:

& .\.venv\Scripts\python.exe server/tests/bizhawk_smoke.py `
  --bizhawk "C:\\path\\to\\BizHawk\\EmuHawk.exe" `
  --rom gb="C:\\path\\to\\roms\\game.gb" gb

Never commit ROMs, savestates, screenshots, logs, command/response files, virtual environments, or generated build output. Check the result before sharing changes:

git status --short --ignored

Troubleshooting

  • Timeout: reload and run .emulator-mcp/bizhawk/bridge.lua, then verify that the MCP client's EMULATOR_MCP_HOME is the same absolute directory.

  • Old or unknown command: reinstall the package in the MCP client's Python environment, run emulator-mcp init .emulator-mcp again, and reload the Lua script in BizHawk. A running MCP client may also need to be restarted.

  • Unsupported system: open a GB/GBC, Genesis, NES, or SNES ROM before loading the bridge.

  • Wrong buttons: call emulator_buttons; BizHawk exposes the core's exact names.

  • Missing state: states are target-specific and live under that target's runtime directory.

Documentation

Resource

Description

AGENTS.md

Repository layout, commands, style, and contribution rules.

pyproject.toml

Package metadata, dependencies, and CLI entry point.

bizhawk/bridge.lua

Portable BizHawk file bridge.

server/mcp_server.py

MCP tools, CLI, scenarios, and runtime protocol.

server/tests

Unit tests and optional BizHawk smoke runner.

scenarios

Target-specific and paired JSON flows.

Contributing

Keep changes focused, add one regression test for non-trivial behavior, run the unit tests and wheel build, and keep emulator artifacts outside version control. Pull requests should include the exact commands run and any relevant BizHawk smoke-test output.

License

MIT. See LICENSE.


Star History Chart

Available Tools

36 tools
bizhawk_bridge_infoC

GB-compatible wrapper for emulator_bridge_info.

Inputs: no named parameters.

bizhawk_create_baselineC

GB-compatible alias for saving a named baseline state.

Inputs: name (required).

bizhawk_holdD

GB-compatible wrapper for emulator_hold.

Inputs: button (required), frames (required).

bizhawk_load_baselineC

GB-compatible alias for loading a named baseline state.

Inputs: name (required).

bizhawk_load_stateC

GB-compatible wrapper for emulator_load_state.

Inputs: name (required).

bizhawk_pressA

Legacy GB alias for tap(button, hold_frames=frames, release_frames=2).

Inputs: button (required), frames.

bizhawk_read_memoryC

GB-compatible wrapper for emulator_read_memory.

Inputs: address (required), domain, length.

bizhawk_releaseC

GB-compatible wrapper for emulator_release.

Inputs: frames.

bizhawk_resetC

GB-compatible wrapper for emulator_reset.

Inputs: settle_frames.

bizhawk_run_scenarioC

GB-compatible wrapper for emulator_run_scenario.

Inputs: path (required).

bizhawk_run_sequenceC

GB-compatible sequence runner.

Inputs: operations (required).

bizhawk_save_stateD

GB-compatible wrapper for emulator_save_state.

Inputs: name (required).

bizhawk_screenshotC

GB-compatible wrapper for emulator_screenshot.

Inputs: name.

bizhawk_statusD

GB-compatible wrapper for emulator_status.

Inputs: no named parameters.

bizhawk_stepC

GB-compatible wrapper for emulator_step.

Inputs: frames.

bizhawk_tapC

GB-compatible wrapper for emulator_tap.

Inputs: button (required), hold_frames, release_frames.

emulator_bridge_infoC

Return bridge.lua version and advertised capabilities for a target.

Inputs: target.

emulator_build_pair_contact_sheetC

Rebuild a pair contact sheet from an existing pair_summary.json.

Inputs: pair_summary_path (required).

emulator_build_pair_diffsC

Rebuild canonical crop diffs and the contact sheet from an existing pair_summary.json.

Inputs: pair_summary_path (required).

emulator_buttonsC

Return current BizHawk joypad button names/states.

Inputs: target.

emulator_create_baselineC

Alias for saving a named baseline state under runtime//states.

Inputs: name, target.

emulator_gb_gpu_snapshotC

Dump raw GB DMG GPU data and optionally render diagnostic PNG panels.

Inputs: name, render, target, output_dir, +1 more.

emulator_holdB

Hold one button for N frames without releasing afterward.

Inputs: button, frames, target.

emulator_load_baselineC

Alias for loading a named baseline state from runtime//states.

Inputs: name, target.

emulator_load_stateC

Load a BizHawk state from the target runtime states directory.

Inputs: name, target.

emulator_read_memoryC

Read a memory range and return its bytes as an uppercase hex string.

Inputs: address (required), domain, length, target.

emulator_releaseC

Release all known buttons for N frames.

Inputs: frames, target.

emulator_resetB

Reboot the emulator core and settle with neutral input for N frames.

Inputs: target, settle_frames.

emulator_run_pair_scenarioC

Run paired GB and MD scenarios and collect their outputs without visual diffing.

Inputs: path (required).

emulator_run_scenarioC

Run a JSON scenario file against a target.

Inputs: path (required), target.

emulator_save_stateC

Save a BizHawk state under the target runtime states directory.

Inputs: name, target.

emulator_screenshotC

Save a screenshot for a target and return its path.

Inputs: name, target.

emulator_statusB

Return emulator status for a target: system, frame, screen size.

Inputs: target.

emulator_stepC

Advance the emulator by N neutral/released frames. Max 600 per call.

Inputs: frames, target.

emulator_tapC

Tap one button with neutral frames before and after.

Inputs: button, target, hold_frames, release_frames.

gb_gpu_snapshotC

Legacy-short alias for emulator_gb_gpu_snapshot(target='gb').

Inputs: render, output_dir, include_raw.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.2/5.0

    Scored across 36 tools

    Disambiguation1/5

    Many tools are aliases or wrappers for the same operations (e.g., emulator_step and bizhawk_step, emulator_tap and bizhawk_tap), making it unclear which to call. Even within the emulator_* group, save_state and create_baseline have blurred boundaries. Several tools appear to do the same thing.

    Naming Consistency2/5

    Naming uses three inconsistent prefix schemes: emulator_, bizhawk_, and legacy aliases like gb_gpu_snapshot. While the core emulator_* tools are consistent, the mix of prefixes and verbose names like emulator_build_pair_contact_sheet breaks the pattern.

    Tool Count2/5

    At 36 tools, many are redundant wrappers or aliases, inflating the count. The actual unique functionality could be covered by fewer tools, making the surface feel bloated and harder to navigate.

    Completeness4/5

    Core emulator operations are well covered: status, stepping, input, reset, screenshot, save/load states, memory read, GPU snapshot, and scenario running. Minor gaps like memory write and ROM loading exist but do not severely hinder the apparent purpose.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    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
      31 npm
      6
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      MCP server for the mGBA Game Boy Advance emulator. Read and write GBA memory, inject button presses, take screenshots, save/load state, and step the emulator through a Lua bridge.
      18
      40 npm
      7
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      MCP server for BizHawk, the multi-system emulator. Drive NES, SNES, GB/GBC/GBA, Genesis, N64, PSX, Saturn and more through one Lua bridge — memory r/w across named domains, joypad input, frame-advance, screenshot, save/load state.
      20
      48 npm
      6
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      An MCP server for Dolphin (GameCube + Wii) — drives memory r/w, controller input (GameCube + Wii Remote), pause/resume/reset, savestates, and frame advance from MCP-compatible clients (Claude Desktop, Claude Code, etc.).
      20
      32 npm
      6
      MIT