mcp-mgba
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.
README
mcp-mgba
An MCP server that exposes the mGBA Game Boy Advance emulator to any MCP-compatible client (Claude Desktop, Claude Code, etc.).
Lets your model read and write GBA memory, inject button presses, take screenshots, and step the emulator — all through a clean tool interface.

Claude driving an in-development homebrew side-scroller through mgba_press_buttons — Start to begin, A to confirm New Game, then Right to walk and A to jump. Each frame is captured via mgba_screenshot.
How it works
+------------------+ stdio +------------------+ TCP :8765 +------------------+
| MCP client | JSON-RPC | mcp-mgba | newline JSON | mGBA emulator |
| (Claude / etc.) | ===========> | (Node.js) | ============> | bridge.lua |
+------------------+ +------------------+ +------------------+
Two pieces:
lua/bridge.lua— runs inside mGBA's scripting engine, opens a loopback TCP server on port 8765dist/index.js— Node.js MCP server, talks to the Lua bridge over TCP, exposes tools over stdio
Requirements
- mGBA 0.10 or newer (with Lua scripting)
- Node.js 18+ (for the MCP server)
Install
Option A — install from npm (recommended)
npm install -g mcp-mgba
Puts mcp-mgba on your PATH. Verify with mcp-mgba --help (it'll print a startup line and wait for stdio — Ctrl+C to exit).
Option B — npx (no install)
npx -y mcp-mgba
Run on demand. Good for trying it out without committing to a global install.
Option C — clone and develop
git clone https://github.com/dmang-dev/mcp-mgba
cd mcp-mgba
npm install # also runs the build via the `prepare` hook
Then reference the absolute path to dist/index.js when registering, or npm install -g . to symlink the bin globally.
Set up the mGBA bridge
- Launch mGBA and load any GBA ROM.
- Open Tools > Scripting…
- Click File > Load script and select
lua/bridge.luafrom this repo.
You should see in the scripting console:
[mcp-mgba] bridge listening on 127.0.0.1:8765
[mcp-mgba] frame callback registered — bridge is active
If you see a bind failed error, the previous instance's socket is still held — quit and relaunch mGBA.
Register with your MCP client
Claude Code (CLI)
claude mcp add mgba --scope user mcp-mgba
(if you used Option B without global install, replace mcp-mgba with node /absolute/path/to/dist/index.js)
Verify:
claude mcp list
# mgba: mcp-mgba - ✓ Connected
Claude Desktop
Edit claude_desktop_config.json:
| Platform | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
Add (assuming Option A — globally installed):
{
"mcpServers": {
"mgba": {
"command": "mcp-mgba"
}
}
}
Or with explicit Node + path (Option B):
{
"mcpServers": {
"mgba": {
"command": "node",
"args": ["/absolute/path/to/mcp-mgba/dist/index.js"]
}
}
}
Restart Claude Desktop after editing.
Other MCP clients
The server speaks standard MCP over stdio. Run mcp-mgba (or node dist/index.js) and connect any MCP client to its stdio.
Configuration
| Env var | Default | Purpose |
|---|---|---|
MGBA_HOST |
127.0.0.1 |
Bridge host to dial |
MGBA_PORT |
8765 |
Bridge port to dial |
Tools
| Tool | Description |
|---|---|
mgba_ping |
Verify bridge connectivity (returns pong) |
mgba_get_info |
Game title, code, frame count |
mgba_read8 / mgba_read16 / mgba_read32 |
Read memory at an address |
mgba_write8 / mgba_write16 / mgba_write32 |
Write to RAM |
mgba_read_range |
Read up to 4096 bytes as a byte array |
mgba_write_range |
Write up to 4096 bytes from a byte array |
mgba_press_buttons |
Queue a button press (FIFO; consecutive calls produce distinct events) |
mgba_advance_frames |
Step the emulator N frames |
mgba_pause / mgba_unpause |
Pause / resume emulation |
mgba_reset |
Reset the loaded ROM |
mgba_screenshot |
Save a PNG of the current display |
mgba_save_state / mgba_load_state |
Save/load emulator state to a slot or path |
See docs/RECIPES.md for end-to-end examples (RAM hunting, snapshot-experiment-restore, side-scroller automation, etc.).
GBA button names
A, B, Select, Start, Right, Left, Up, Down, R, L
GBA address space (cheat sheet)
| Range | Region |
|---|---|
0x02000000 |
EWRAM (256 KiB, general) |
0x03000000 |
IWRAM (32 KiB, fast) |
0x04000000 |
I/O registers |
0x05000000 |
Palette RAM |
0x06000000 |
VRAM |
0x07000000 |
OAM |
0x08000000 |
ROM (read-only) |
Troubleshooting
| Symptom | Cause / Fix |
|---|---|
Cannot reach mGBA bridge at 127.0.0.1:8765 |
mGBA isn't running, or bridge.lua isn't loaded — open Tools > Scripting and load it |
bind failed — port 8765 may already be in use |
A previous mGBA instance still holds the socket; quit and relaunch mGBA |
| Tool calls hang | The bridge script may have errored out silently after a hot-reload — check the mGBA scripting console |
| Tools missing in Claude after install | Restart your MCP client; Claude only enumerates servers on startup |
Tool calls return data shaped like an old version after editing bridge.lua and choosing Load Script again |
mGBA doesn't fully tear down a previous script when you reload. The new script's bind() may succeed but the old frame callback keeps serving requests. Fix: quit mGBA entirely, relaunch, load the ROM, then load bridge.lua once. Check the console for the frame callback registered line — there should be exactly one. |
attempt to index a nil value (global 'emu') at script load |
mGBA's emu global only exists once a ROM is loaded. Load any ROM first, then load bridge.lua. (Or load the script first; capability detection will defer until a ROM is loaded.) |
emu:foo not available on this mGBA build for pause, unpause, frameAdvance, etc. |
This particular build of mGBA doesn't expose that method. The bridge feature-detects on the first frame; check mgba_get_info for the full capabilities map. For frameAdvance, the bridge falls back to runFrame then step automatically. |
read8/16/32 returns "invoking failed" intermittently |
Known mGBA Lua quirk — the typed read methods are flaky via pcall from the frame callback. The bridge already routes read8/16/32 through the more reliable readRange internally; if you still see this on a write, the retry loop usually clears it within a few attempts. |
Multiple press_buttons calls don't seem to register as distinct events |
Older mgba_press_buttons (≤0.1.0) had this bug; v0.2.0+ uses a FIFO queue. Make sure you've upgraded with npm install -g mcp-mgba and restarted your MCP client. |
Development
npm install
npm run dev # tsc --watch — autobuilds on src/ changes
The Lua side (lua/bridge.lua and lua/json.lua) needs no build step. Edit and reload via mGBA's File > Load script.
License
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。